From 81bf1d91e9d954411e06ad793ab403765ef53ad0 Mon Sep 17 00:00:00 2001 From: BlackMATov Date: Wed, 20 Aug 2025 23:27:08 +0700 Subject: [PATCH 01/12] Revert "temp remove pairs to merge other changes to dev" --- README.md | 140 ++- develop/all.lua | 4 + develop/fuzzing/wildcard_fuzz.lua | 254 +++++ develop/samples/relations.lua | 79 ++ develop/testing/pairs_tests.lua | 1607 +++++++++++++++++++++++++++ evolved.lua | 1677 +++++++++++++++++++++++++---- 6 files changed, 3573 insertions(+), 188 deletions(-) create mode 100644 develop/fuzzing/wildcard_fuzz.lua create mode 100644 develop/samples/relations.lua create mode 100644 develop/testing/pairs_tests.lua diff --git a/README.md b/README.md index 9eceff9..e2358b5 100644 --- a/README.md +++ b/README.md @@ -53,7 +53,8 @@ - [Cheat Sheet](#cheat-sheet) - [Aliases](#aliases) - [Predefs](#predefs) - - [Functions](#functions) + - [Core Functions](#core-functions) + - [Relation Functions](#relation-functions) - [Classes](#classes) - [Chunk](#chunk) - [Builder](#builder) @@ -1048,14 +1049,20 @@ remove_hook :: {entity, fragment, component} each_state :: implementation-specific execute_state :: implementation-specific +primaries_state :: implementation-specific +secondaries_state :: implementation-specific each_iterator :: {each_state? -> fragment?, component?} execute_iterator :: {execute_state? -> chunk?, entity[]?, integer?} +primaries_iterator :: {primaries_state? -> fragment?, component?} +secondaries_iterator :: {secondaries_state? -> fragment?, component?} ``` ### Predefs ``` +ANY :: fragment + TAG :: fragment NAME :: fragment @@ -1091,7 +1098,7 @@ DESTRUCTION_POLICY_DESTROY_ENTITY :: id DESTRUCTION_POLICY_REMOVE_FRAGMENT :: id ``` -### Functions +### Core Functions ``` id :: integer? -> id... @@ -1139,6 +1146,25 @@ debug_mode :: boolean -> () collect_garbage :: () ``` +### Relation Functions + +``` +pair :: id, id -> id +unpair :: id -> id, id + +is_pair :: id -> boolean +is_wildcard :: id -> boolean + +primary :: entity, fragment, integer? -> fragment?, component? +secondary :: entity, fragment, integer? -> fragment?, component? + +primaries :: entity, fragment -> {primaries_state? -> fragment?, component?}, primaries_state? +secondaries :: entity, fragment -> {secondaries_state? -> fragment?, component?}, secondaries_state? + +primary_count :: entity, fragment -> integer +secondary_count :: entity, fragment -> integer +``` + ### Classes #### Chunk @@ -1233,6 +1259,8 @@ builder_mt:destruction_policy :: id -> builder ## Predefs +### `evolved.ANY` + ### `evolved.TAG` ### `evolved.NAME` @@ -1281,7 +1309,7 @@ builder_mt:destruction_policy :: id -> builder ### `evolved.DESTRUCTION_POLICY_REMOVE_FRAGMENT` -## Functions +## Core Functions ### `evolved.id` @@ -1549,6 +1577,112 @@ function evolved.debug_mode(yesno) end function evolved.collect_garbage() end ``` +## Relation Functions + +### `evolved.pair` + +```lua +---@param primary evolved.id +---@param secondary evolved.id +---@return evolved.id pair +---@nodiscard +function evolved.pair(primary, secondary) end +``` + +### `evolved.unpair` + +```lua +---@param pair evolved.id +---@return evolved.id primary +---@return evolved.id secondary +---@nodiscard +function evolved.unpair(pair) end +``` + +### `evolved.is_pair` + +```lua +---@param id evolved.id +---@return boolean +---@nodiscard +function evolved.is_pair(id) end +``` + +### `evolved.is_wildcard` + +```lua +---@param id evolved.id +---@return boolean +---@nodiscard +function evolved.is_wildcard(id) end +``` + +### `evolved.primary` + +```lua +---@param entity evolved.entity +---@param secondary evolved.fragment +---@param index? integer +---@return evolved.fragment? primary +---@return evolved.component? component +---@nodiscard +function evolved.primary(entity, secondary, index) end +``` + +### `evolved.secondary` + +```lua +---@param entity evolved.entity +---@param primary evolved.fragment +---@param index? integer +---@return evolved.fragment? secondary +---@return evolved.component? component +---@nodiscard +function evolved.secondary(entity, primary, index) end +``` + +### `evolved.primaries` + +```lua +---@param entity evolved.entity +---@param secondary evolved.fragment +---@return evolved.primaries_iterator iterator +---@return evolved.primaries_state? iterator_state +---@nodiscard +function evolved.primaries(entity, secondary) end +``` + +### `evolved.secondaries` + +```lua +---@param entity evolved.entity +---@param primary evolved.fragment +---@return evolved.secondaries_iterator iterator +---@return evolved.secondaries_state? iterator_state +---@nodiscard +function evolved.secondaries(entity, primary) end +``` + +### `evolved.primary_count` + +```lua +---@param entity evolved.entity +---@param secondary evolved.fragment +---@return integer +---@nodiscard +function evolved.primary_count(entity, secondary) end +``` + +### `evolved.secondary_count` + +```lua +---@param entity evolved.entity +---@param primary evolved.fragment +---@return integer +---@nodiscard +function evolved.secondary_count(entity, primary) end +``` + ## Classes ### Chunk diff --git a/develop/all.lua b/develop/all.lua index bd34bf9..b5d6025 100644 --- a/develop/all.lua +++ b/develop/all.lua @@ -1,6 +1,8 @@ +require 'develop.samples.relations' require 'develop.samples.systems' require 'develop.testing.name_tests' +require 'develop.testing.pairs_tests' require 'develop.testing.requires_fragment_tests' require 'develop.testing.system_as_query_tests' @@ -23,3 +25,5 @@ print '----------------------------------------' basics.describe_fuzz 'develop.fuzzing.requires_fuzz' print '----------------------------------------' basics.describe_fuzz 'develop.fuzzing.unique_fuzz' +print '----------------------------------------' +basics.describe_fuzz 'develop.fuzzing.wildcard_fuzz' diff --git a/develop/fuzzing/wildcard_fuzz.lua b/develop/fuzzing/wildcard_fuzz.lua new file mode 100644 index 0000000..916afc2 --- /dev/null +++ b/develop/fuzzing/wildcard_fuzz.lua @@ -0,0 +1,254 @@ +local evo = require 'evolved' + +evo.debug_mode(true) + +--- +--- +--- +--- +--- + +local __table_unpack = (function() + ---@diagnostic disable-next-line: deprecated + return table.unpack or unpack +end)() + +--- +--- +--- +--- +--- + +local all_entity_list = {} ---@type evolved.entity[] +local all_fragment_list = {} ---@type evolved.fragment[] + +for i = 1, math.random(1, 5) do + local fragment_builder = evo.builder() + + if math.random(1, 3) == 1 then + fragment_builder:explicit() + end + + if math.random(1, 3) == 1 then + if math.random(1, 2) == 1 then + fragment_builder:destruction_policy(evo.DESTRUCTION_POLICY_DESTROY_ENTITY) + else + fragment_builder:destruction_policy(evo.DESTRUCTION_POLICY_REMOVE_FRAGMENT) + end + end + + all_fragment_list[i] = fragment_builder:spawn() +end + +for i = 1, math.random(50, 100) do + local entity_builder = evo.builder() + + for _ = 0, math.random(0, #all_fragment_list) do + if math.random(1, 2) == 1 then + local fragment = all_fragment_list[math.random(1, #all_fragment_list)] + entity_builder:set(fragment) + else + local primary = all_fragment_list[math.random(1, #all_fragment_list)] + local secondary = all_fragment_list[math.random(1, #all_fragment_list)] + entity_builder:set(evo.pair(primary, secondary)) + end + end + + all_entity_list[i] = entity_builder:spawn() +end + +--- +--- +--- +--- +--- + +for _ = 1, math.random(1, 100) do + local query_builder = evo.builder() + + local query_include_set = {} ---@type table + local query_include_list = {} ---@type evolved.entity[] + local query_include_count = 0 ---@type integer + + local query_exclude_set = {} ---@type table + local query_exclude_list = {} ---@type evolved.entity[] + local query_exclude_count = 0 ---@type integer + + for _ = 1, math.random(0, 2) do + if math.random(1, 2) == 1 then + local fragment = all_fragment_list[math.random(1, #all_fragment_list)] + + query_builder:include(fragment) + + if not query_include_set[fragment] then + query_include_count = query_include_count + 1 + query_include_set[fragment] = query_include_count + query_include_list[query_include_count] = fragment + end + else + local primary = all_fragment_list[math.random(1, #all_fragment_list)] + local secondary = all_fragment_list[math.random(1, #all_fragment_list)] + + if math.random(1, 3) == 1 then + primary = evo.ANY + end + + if math.random(1, 3) == 1 then + secondary = evo.ANY + end + + local pair = evo.pair(primary, secondary) + + query_builder:include(pair) + + if not query_include_set[pair] then + query_include_count = query_include_count + 1 + query_include_set[pair] = query_include_count + query_include_list[query_include_count] = pair + end + end + end + + for _ = 1, math.random(0, 2) do + if math.random(1, 2) == 1 then + local fragment = all_fragment_list[math.random(1, #all_fragment_list)] + + query_builder:exclude(fragment) + + if not query_exclude_set[fragment] then + query_exclude_count = query_exclude_count + 1 + query_exclude_set[fragment] = query_exclude_count + query_exclude_list[query_exclude_count] = fragment + end + else + local primary = all_fragment_list[math.random(1, #all_fragment_list)] + local secondary = all_fragment_list[math.random(1, #all_fragment_list)] + + if math.random(1, 3) == 1 then + primary = evo.ANY + end + + if math.random(1, 3) == 1 then + secondary = evo.ANY + end + + local pair = evo.pair(primary, secondary) + + query_builder:exclude(pair) + + if not query_exclude_set[pair] then + query_exclude_count = query_exclude_count + 1 + query_exclude_set[pair] = query_exclude_count + query_exclude_list[query_exclude_count] = pair + end + end + end + + local query_entity_set = {} ---@type table + local query_entity_count = 0 ---@type integer + + do + local query = query_builder:spawn() + + for chunk, entity_list, entity_count in evo.execute(query) do + if not chunk:has(evo.INTERNAL) then + for i = 1, entity_count do + local entity = entity_list[i] + assert(not query_entity_set[entity]) + query_entity_count = query_entity_count + 1 + query_entity_set[entity] = query_entity_count + end + end + end + + if query_entity_set[query] then + query_entity_set[query] = nil + query_entity_count = query_entity_count - 1 + end + + evo.destroy(query) + end + + do + local expected_entity_count = 0 + + for _, entity in ipairs(all_entity_list) do + local is_entity_expected = + not evo.empty(entity) and + evo.has_all(entity, __table_unpack(query_include_list)) and + not evo.has_any(entity, __table_unpack(query_exclude_list)) + + for fragment in evo.each(entity) do + if evo.has(fragment, evo.EXPLICIT) then + local is_fragment_included = + query_include_set[fragment] ~= nil or + query_include_set[evo.pair(fragment, evo.ANY)] ~= nil + + if not is_fragment_included then + is_entity_expected = false + break + end + end + end + + if is_entity_expected then + assert(query_entity_set[entity]) + expected_entity_count = expected_entity_count + 1 + else + assert(not query_entity_set[entity]) + end + end + + for _, entity in ipairs(all_fragment_list) do + local is_entity_expected = + not evo.empty(entity) and + evo.has_all(entity, __table_unpack(query_include_list)) and + not evo.has_any(entity, __table_unpack(query_exclude_list)) + + for fragment in evo.each(entity) do + if evo.has(fragment, evo.EXPLICIT) then + is_entity_expected = is_entity_expected and + (query_include_set[fragment] ~= nil) or + (evo.is_pair(fragment) and query_include_set[evo.pair(fragment, evo.ANY)] ~= nil) + end + end + + if is_entity_expected then + assert(query_entity_set[entity]) + expected_entity_count = expected_entity_count + 1 + else + assert(not query_entity_set[entity]) + end + end + + assert(query_entity_count == expected_entity_count) + end +end + +--- +--- +--- +--- +--- + +if math.random(1, 2) == 1 then + evo.collect_garbage() +end + +if math.random(1, 2) == 1 then + evo.destroy(__table_unpack(all_entity_list)) + if math.random(1, 2) == 1 then + evo.collect_garbage() + end + evo.destroy(__table_unpack(all_fragment_list)) +else + evo.destroy(__table_unpack(all_fragment_list)) + if math.random(1, 2) == 1 then + evo.collect_garbage() + end + evo.destroy(__table_unpack(all_entity_list)) +end + +if math.random(1, 2) == 1 then + evo.collect_garbage() +end diff --git a/develop/samples/relations.lua b/develop/samples/relations.lua new file mode 100644 index 0000000..6311fbe --- /dev/null +++ b/develop/samples/relations.lua @@ -0,0 +1,79 @@ +---@diagnostic disable: unused-local + +local evo = require 'evolved' + +evo.debug_mode(true) + +local fragments = { + planet = evo.builder() + :name('planet') + :tag() + :spawn(), + spaceship = evo.builder() + :name('spaceship') + :tag() + :spawn(), +} + +local relations = { + docked_to = evo.builder() + :name('docked_to') + :tag() + :spawn(), +} + +local planets = { + mars = evo.builder() + :name('Mars') + :set(fragments.planet) + :spawn(), + venus = evo.builder() + :name('Venus') + :set(fragments.planet) + :spawn(), +} + +local spaceships = { + falcon = evo.builder() + :name('Millennium Falcon') + :set(fragments.spaceship) + :set(evo.pair(relations.docked_to, planets.mars)) + :spawn(), + enterprise = evo.builder() + :name('USS Enterprise') + :set(fragments.spaceship) + :set(evo.pair(relations.docked_to, planets.venus)) + :spawn(), +} + +local queries = { + all_docked_spaceships = evo.builder() + :include(fragments.spaceship) + :include(evo.pair(relations.docked_to, evo.ANY)) + :spawn(), + docked_spaceships_to_mars = evo.builder() + :include(fragments.spaceship) + :include(evo.pair(relations.docked_to, planets.mars)) + :spawn(), + +} + +print '-= | All Docked Spaceships | =-' + +for chunk, entity_list, entity_count in evo.execute(queries.all_docked_spaceships) do + for i = 1, entity_count do + local entity = entity_list[i] + local planet = evo.secondary(entity, relations.docked_to) + print(string.format('%s is docked to %s', evo.name(entity), evo.name(planet))) + end +end + +print '-= | Docked Spaceships to Mars | =-' + +for chunk, entity_list, entity_count in evo.execute(queries.docked_spaceships_to_mars) do + for i = 1, entity_count do + local entity = entity_list[i] + local planet = evo.secondary(entity, relations.docked_to) + print(string.format('%s is docked to %s', evo.name(entity), evo.name(planet))) + end +end diff --git a/develop/testing/pairs_tests.lua b/develop/testing/pairs_tests.lua new file mode 100644 index 0000000..e5982bd --- /dev/null +++ b/develop/testing/pairs_tests.lua @@ -0,0 +1,1607 @@ +local evo = require 'evolved' + +do + local p1, s1 = evo.id(2) + local pair1 = evo.pair(p1, s1) + local p2, s2 = evo.unpair(pair1) + assert(p1 == p2 and s1 == s2) +end + +do + local p, s1, s2 = evo.id(3) + + local e1 = evo.id() + evo.set(e1, evo.pair(p, s1), 11) + + local e12 = evo.id() + evo.set(e12, evo.pair(p, s1), 21) + evo.set(e12, evo.pair(p, s2), 42) + + assert(evo.has(e1, evo.pair(p, s1))) + assert(evo.get(e1, evo.pair(p, s1)) == 11) + assert(evo.has(e12, evo.pair(p, s1))) + assert(evo.get(e12, evo.pair(p, s1)) == 21) + + assert(not evo.has(e1, evo.pair(p, s2))) + assert(evo.get(e1, evo.pair(p, s2)) == nil) + assert(evo.has(e12, evo.pair(p, s2))) + assert(evo.get(e12, evo.pair(p, s2)) == 42) + + assert(evo.has(e1, evo.pair(p, evo.ANY))) + assert(evo.has(e1, evo.pair(evo.ANY, s1))) + assert(not evo.has(e1, evo.pair(evo.ANY, s2))) + assert(evo.has(e12, evo.pair(p, evo.ANY))) + assert(evo.has(e12, evo.pair(evo.ANY, s1))) + assert(evo.has(e12, evo.pair(evo.ANY, s2))) + + assert(not evo.has_all(e1, evo.pair(evo.ANY, s1), evo.pair(evo.ANY, s2))) + assert(evo.has_any(e1, evo.pair(evo.ANY, s1), evo.pair(evo.ANY, s2))) + assert(evo.has_all(e12, evo.pair(evo.ANY, s1), evo.pair(evo.ANY, s2))) + assert(evo.has_any(e12, evo.pair(evo.ANY, s1), evo.pair(evo.ANY, s2))) +end + +do + local p1, p2, s = evo.id(3) + + local e1 = evo.id() + evo.set(e1, evo.pair(p1, s), 11) + + local e12 = evo.id() + evo.set(e12, evo.pair(p1, s), 21) + evo.set(e12, evo.pair(p2, s), 42) + + assert(evo.has(e1, evo.pair(p1, s))) + assert(evo.get(e1, evo.pair(p1, s)) == 11) + assert(evo.has(e12, evo.pair(p1, s))) + assert(evo.get(e12, evo.pair(p1, s)) == 21) + + assert(not evo.has(e1, evo.pair(p2, s))) + assert(evo.get(e1, evo.pair(p2, s)) == nil) + assert(evo.has(e12, evo.pair(p2, s))) + assert(evo.get(e12, evo.pair(p2, s)) == 42) + + assert(evo.has(e1, evo.pair(p1, evo.ANY))) + assert(not evo.has(e1, evo.pair(p2, evo.ANY))) + assert(evo.has(e1, evo.pair(evo.ANY, s))) + assert(evo.has(e12, evo.pair(p1, evo.ANY))) + assert(evo.has(e12, evo.pair(p2, evo.ANY))) + assert(evo.has(e12, evo.pair(evo.ANY, s))) + + assert(not evo.has_all(e1, evo.pair(p1, evo.ANY), evo.pair(p2, evo.ANY))) + assert(evo.has_any(e1, evo.pair(p1, evo.ANY), evo.pair(p2, evo.ANY))) + assert(evo.has_all(e12, evo.pair(p1, evo.ANY), evo.pair(p2, evo.ANY))) + assert(evo.has_any(e12, evo.pair(p1, evo.ANY), evo.pair(p2, evo.ANY))) +end + +do + local p1, s1, p2, s2 = evo.id(4) + evo.set(p1, s1) + evo.set(s1, p1) + evo.set(p2, s2) + assert(not evo.empty(evo.pair(p1, s1))) + assert(not evo.empty(evo.pair(p2, s2))) + assert(not evo.empty_all(evo.pair(p1, s1), evo.pair(p2, s2))) + assert(not evo.empty_any(evo.pair(p1, s1), evo.pair(p2, s2))) + assert(not evo.empty_all(evo.pair(p1, s1), evo.pair(p2, s2), p1)) + assert(not evo.empty_any(evo.pair(p1, s1), evo.pair(p2, s2), p1)) + assert(not evo.empty_all(evo.pair(p1, s1), evo.pair(p2, s2), s2)) + assert(evo.empty_any(evo.pair(p1, s1), evo.pair(p2, s2), s2)) +end + +do + local p1, s1 = evo.id(2) + evo.set(p1, s1) + evo.set(s1, p1) + assert(not evo.has(evo.pair(p1, s1), p1)) + assert(evo.has(evo.pair(p1, s1), s1)) + assert(not evo.has_all(evo.pair(p1, s1), p1, s1)) + assert(evo.has_any(evo.pair(p1, s1), p1, s1)) + assert(evo.get(evo.pair(p1, s1), p1) == nil) + assert(evo.get(evo.pair(p1, s1), s1) == true) +end + +do + local p, s1, s2 = evo.id(3) + + do + local e = evo.builder() + :set(evo.pair(p, s1), 21) + :set(evo.pair(p, s2), 42) + :spawn() + + evo.remove(e, evo.pair(p, s1)) + + assert(not evo.has(e, evo.pair(p, s1))) + assert(evo.get(e, evo.pair(p, s1)) == nil) + + assert(evo.has(e, evo.pair(p, s2))) + assert(evo.get(e, evo.pair(p, s2)) == 42) + + evo.remove(e, evo.pair(p, s2)) + + assert(not evo.has(e, evo.pair(p, s2))) + assert(evo.get(e, evo.pair(p, s2)) == nil) + + assert(not evo.has(e, evo.pair(p, s2))) + assert(evo.get(e, evo.pair(p, s2)) == nil) + end + + do + local e = evo.builder() + :set(evo.pair(p, s1), 21) + :set(evo.pair(p, s2), 42) + :spawn() + + evo.remove(e, evo.pair(p, s2)) + + assert(evo.has(e, evo.pair(p, s1))) + assert(evo.get(e, evo.pair(p, s1)) == 21) + + assert(not evo.has(e, evo.pair(p, s2))) + assert(evo.get(e, evo.pair(p, s2)) == nil) + + evo.remove(e, evo.pair(p, s1)) + + assert(not evo.has(e, evo.pair(p, s1))) + assert(evo.get(e, evo.pair(p, s1)) == nil) + + assert(not evo.has(e, evo.pair(p, s2))) + assert(evo.get(e, evo.pair(p, s2)) == nil) + end +end + +do + local p1, p2, s1, s2 = evo.id(4) + + do + local e = evo.builder() + :set(evo.pair(p1, s1), 11) + :set(evo.pair(p1, s2), 12) + :set(evo.pair(p2, s1), 21) + :set(evo.pair(p2, s2), 22) + :spawn() + + evo.remove(e, evo.pair(p1, evo.ANY)) + + assert(not evo.has(e, evo.pair(p1, s1))) + assert(not evo.has(e, evo.pair(p1, s2))) + assert(not evo.has(e, evo.pair(p1, evo.ANY))) + + assert(evo.has(e, evo.pair(p2, s1))) + assert(evo.get(e, evo.pair(p2, s1)) == 21) + assert(evo.has(e, evo.pair(p2, s2))) + assert(evo.get(e, evo.pair(p2, s2)) == 22) + assert(evo.has(e, evo.pair(p2, evo.ANY))) + end + + do + local e = evo.builder() + :set(evo.pair(p1, s1), 11) + :set(evo.pair(p1, s2), 12) + :set(evo.pair(p2, s1), 21) + :set(evo.pair(p2, s2), 22) + :spawn() + + evo.remove(e, evo.pair(p2, evo.ANY)) + + assert(not evo.has(e, evo.pair(p2, s1))) + assert(not evo.has(e, evo.pair(p2, s2))) + assert(not evo.has(e, evo.pair(p2, evo.ANY))) + + assert(evo.has(e, evo.pair(p1, s1))) + assert(evo.get(e, evo.pair(p1, s1)) == 11) + assert(evo.has(e, evo.pair(p1, s2))) + assert(evo.get(e, evo.pair(p1, s2)) == 12) + assert(evo.has(e, evo.pair(p1, evo.ANY))) + end + + do + local e = evo.builder() + :set(evo.pair(p1, s1), 11) + :set(evo.pair(p1, s2), 12) + :set(evo.pair(p2, s1), 21) + :set(evo.pair(p2, s2), 22) + :spawn() + + evo.remove(e, evo.pair(evo.ANY, s1)) + + assert(not evo.has(e, evo.pair(p1, s1))) + assert(not evo.has(e, evo.pair(p2, s1))) + assert(not evo.has(e, evo.pair(evo.ANY, s1))) + + assert(evo.has(e, evo.pair(p1, s2))) + assert(evo.get(e, evo.pair(p1, s2)) == 12) + assert(evo.has(e, evo.pair(p2, s2))) + assert(evo.get(e, evo.pair(p2, s2)) == 22) + end + + do + local e = evo.builder() + :set(evo.pair(p1, s1), 11) + :set(evo.pair(p1, s2), 12) + :set(evo.pair(p2, s1), 21) + :set(evo.pair(p2, s2), 22) + :spawn() + + evo.remove(e, evo.pair(evo.ANY, s2)) + + assert(not evo.has(e, evo.pair(p1, s2))) + assert(not evo.has(e, evo.pair(p2, s2))) + assert(not evo.has(e, evo.pair(evo.ANY, s2))) + + assert(evo.has(e, evo.pair(p1, s1))) + assert(evo.get(e, evo.pair(p1, s1)) == 11) + assert(evo.has(e, evo.pair(p2, s1))) + assert(evo.get(e, evo.pair(p2, s1)) == 21) + end + + do + local e = evo.builder() + :set(evo.pair(p1, s1), 11) + :set(evo.pair(p1, s2), 12) + :set(evo.pair(p2, s1), 21) + :set(evo.pair(p2, s2), 22) + :set(p1, s1) + :set(p2, s2) + :spawn() + + evo.remove(e, evo.pair(evo.ANY, evo.ANY)) + + assert(not evo.has(e, evo.pair(p1, s1))) + assert(not evo.has(e, evo.pair(p1, s2))) + assert(not evo.has(e, evo.pair(p2, s1))) + assert(not evo.has(e, evo.pair(p2, s2))) + + assert(evo.has(e, p1) and evo.get(e, p1) == s1) + assert(evo.has(e, p2) and evo.get(e, p2) == s2) + end +end + +do + local p1, s1, p2, s2 = evo.id(4) + + local e = evo.builder() + :set(evo.pair(p1, s1), 42) + :spawn() + + evo.remove(e, evo.pair(p2, evo.ANY)) + evo.remove(e, evo.pair(evo.ANY, s2)) + + assert(evo.has(e, evo.pair(p1, s1))) + assert(evo.get(e, evo.pair(p1, s1)) == 42) + + evo.remove(e, evo.pair(p1, s1)) + + assert(not evo.has(e, evo.pair(p1, s1))) + assert(evo.get(e, evo.pair(p1, s1)) == nil) +end + +do + local f1, f2, f3, p1, s1, p2, s2 = evo.id(7) + evo.set(f1, evo.REQUIRES, { f2 }) + evo.set(f2, evo.DEFAULT, 84) + evo.set(f2, evo.REQUIRES, { evo.pair(p2, s2) }) + evo.set(p1, evo.REQUIRES, { f3 }) + evo.set(s1, evo.REQUIRES, { f3 }) + evo.set(p2, evo.REQUIRES, { f3 }) + evo.set(s2, evo.REQUIRES, { f3 }) + + local e = evo.builder() + :set(f1, 21) + :set(evo.pair(p1, s1), 42) + :spawn() + + assert(evo.has(e, evo.pair(p1, s1))) + assert(evo.get(e, evo.pair(p1, s1)) == 42) + + assert(evo.has(e, evo.pair(p2, s2))) + assert(evo.get(e, evo.pair(p2, s2)) == true) + + assert(evo.has(e, f1)) + assert(evo.get(e, f1) == 21) + + assert(evo.has(e, f2)) + assert(evo.get(e, f2) == 84) + + assert(evo.has(e, f3)) + assert(evo.get(e, f3) == true) +end + +do + local p1, p2, s1, s2 = evo.id(4) + + do + local e1 = evo.builder() + :set(evo.pair(p1, s1)) + :set(evo.pair(p1, s2)) + :spawn() + + local e2 = evo.clone(e1) + + evo.remove(e1, evo.pair(p1, evo.ANY)) + evo.remove(e2, evo.pair(p1, evo.ANY)) + assert(evo.empty_all(e1, e2)) + end + + do + local e1 = evo.builder() + :set(evo.pair(p1, s1)) + :set(evo.pair(p2, s1)) + :spawn() + + local e2 = evo.clone(e1) + + evo.remove(e1, evo.pair(evo.ANY, s1)) + evo.remove(e2, evo.pair(evo.ANY, s1)) + assert(evo.empty_all(e1, e2)) + end + + do + local e1 = evo.builder() + :set(evo.pair(p1, s1)) + :set(evo.pair(p1, s2)) + :set(evo.pair(p2, s1)) + :set(evo.pair(p2, s2)) + :spawn() + + local e2 = evo.clone(e1) + + evo.remove(e1, evo.pair(evo.ANY, evo.ANY)) + evo.remove(e2, evo.pair(evo.ANY, evo.ANY)) + assert(evo.empty_all(e1, e2)) + end +end + +do + local f, p1, p2, s1, s2 = evo.id(5) + + do + local e1 = evo.builder() + :set(f, 42) + :set(evo.pair(p1, s1)) + :set(evo.pair(p1, s2)) + :spawn() + + local e2 = evo.clone(e1) + + evo.remove(e1, evo.pair(p1, evo.ANY)) + evo.remove(e2, evo.pair(p1, evo.ANY)) + + assert(evo.has(e1, f) and evo.has(e2, f)) + assert(not evo.has(e1, evo.pair(evo.ANY, evo.ANY))) + end + + do + local e1 = evo.builder() + :set(f, 42) + :set(evo.pair(p1, s1)) + :set(evo.pair(p2, s1)) + :spawn() + + local e2 = evo.clone(e1) + + evo.remove(e1, evo.pair(evo.ANY, s1)) + evo.remove(e2, evo.pair(evo.ANY, s1)) + + assert(evo.has(e1, f) and evo.has(e2, f)) + assert(not evo.has(e1, evo.pair(evo.ANY, evo.ANY))) + end + + do + local e1 = evo.builder() + :set(f, 42) + :set(evo.pair(p1, s1)) + :set(evo.pair(p1, s2)) + :set(evo.pair(p2, s1)) + :set(evo.pair(p2, s2)) + :spawn() + + local e2 = evo.clone(e1) + + evo.remove(e1, evo.pair(evo.ANY, evo.ANY)) + evo.remove(e2, evo.pair(evo.ANY, evo.ANY)) + + assert(evo.has(e1, f) and evo.has(e2, f)) + assert(not evo.has(e1, evo.pair(evo.ANY, evo.ANY))) + end +end + +do + do + local p, s = evo.id(2) + evo.set(p, evo.NAME, 'p') + evo.set(s, evo.NAME, 's') + local ps_chunk = evo.chunk(evo.pair(p, s)) + assert(tostring(ps_chunk) == '<${p,s}>') + end + do + local p, s = evo.id(2) + local ps_chunk = evo.chunk(evo.pair(p, s)) + evo.set(p, evo.NAME, 'p') + evo.set(s, evo.NAME, 's') + evo.destroy(p) + assert(tostring(ps_chunk) ~= '<${p,s}>') + end + do + local p, s = evo.id(2) + local ps_chunk = evo.chunk(evo.pair(p, s)) + evo.set(p, evo.NAME, 'p') + evo.set(s, evo.NAME, 's') + evo.destroy(s) + assert(tostring(ps_chunk) ~= '<${p,s}>') + end + do + local p, s = evo.id(2) + local ps_chunk = evo.chunk(evo.pair(p, s)) + evo.set(p, evo.NAME, 'p') + evo.set(s, evo.NAME, 's') + evo.destroy(p, s) + assert(tostring(ps_chunk) ~= '<${p,s}>') + end +end + +do + do + local p, s = evo.id(2) + local ps = evo.pair(evo.ANY, s) + local e = evo.id() + evo.set(e, p, 42) + evo.destroy(s) + evo.remove(e, ps) + end + + do + local p, s = evo.id(2) + local ps = evo.pair(p, evo.ANY) + local e = evo.id() + evo.set(e, s, 42) + evo.destroy(p) + evo.remove(e, ps) + end +end + +do + local p, s = evo.id(2) + + local e = evo.id() + assert(not evo.has(e, evo.pair(p, s))) + assert(not evo.has(e, evo.pair(p, evo.ANY))) + assert(not evo.has(e, evo.pair(evo.ANY, s))) + assert(not evo.has(e, evo.pair(evo.ANY, evo.ANY))) + + evo.set(e, p) + assert(not evo.has(e, evo.pair(p, s))) + assert(not evo.has(e, evo.pair(p, evo.ANY))) + assert(not evo.has(e, evo.pair(evo.ANY, s))) + assert(not evo.has(e, evo.pair(evo.ANY, evo.ANY))) + + evo.set(e, s) + assert(not evo.has(e, evo.pair(p, s))) + assert(not evo.has(e, evo.pair(p, evo.ANY))) + assert(not evo.has(e, evo.pair(evo.ANY, s))) + assert(not evo.has(e, evo.pair(evo.ANY, evo.ANY))) + + evo.set(e, evo.pair(p, s)) + assert(evo.has(e, evo.pair(p, s))) + assert(evo.has(e, evo.pair(p, evo.ANY))) + assert(evo.has(e, evo.pair(evo.ANY, s))) + assert(evo.has(e, evo.pair(evo.ANY, evo.ANY))) +end + +do + local p1, s1, p2, s2 = evo.id(4) + + local e = evo.builder():set(evo.pair(p1, s1)):spawn() + assert(evo.has(e, evo.pair(p1, s1))) + assert(evo.has(e, evo.pair(p1, evo.ANY))) + assert(evo.has(e, evo.pair(evo.ANY, s1))) + assert(evo.has(e, evo.pair(evo.ANY, evo.ANY))) + assert(not evo.has(e, evo.pair(p1, s2))) + assert(not evo.has(e, evo.pair(p2, s1))) + assert(not evo.has(e, evo.pair(p2, s2))) + assert(not evo.has(e, evo.pair(p2, evo.ANY))) + assert(not evo.has(e, evo.pair(evo.ANY, s2))) + + evo.set(e, evo.pair(p2, s2)) + + assert(evo.has(e, evo.pair(p1, s1))) + assert(evo.has(e, evo.pair(p1, evo.ANY))) + assert(evo.has(e, evo.pair(evo.ANY, s1))) + assert(evo.has(e, evo.pair(evo.ANY, evo.ANY))) + assert(not evo.has(e, evo.pair(p1, s2))) + assert(not evo.has(e, evo.pair(p2, s1))) + assert(evo.has(e, evo.pair(p2, s2))) + assert(evo.has(e, evo.pair(p2, evo.ANY))) + assert(evo.has(e, evo.pair(evo.ANY, s2))) +end + +do + local p1, s1, s2 = evo.id(3) + + do + local e = evo.builder() + :set(evo.pair(p1, s1), 42) + :spawn() + + evo.set(e, evo.pair(p1, s1), 84) + assert(evo.get(e, evo.pair(p1, s1)) == 84) + assert(evo.get(e, evo.pair(p1, s2)) == nil) + + evo.set(e, evo.pair(p1, s2), 42) + assert(evo.get(e, evo.pair(p1, s1)) == 84) + assert(evo.get(e, evo.pair(p1, s2)) == 42) + end +end + +do + local p1, s1, p2, s2 = evo.id(4) + + do + local e = evo.builder() + :set(evo.pair(p1, s1), 42) + :set(evo.pair(p1, s2), 84) + :set(evo.pair(p2, s1), 21) + :set(evo.pair(p2, s2), 63) + :spawn() + + evo.remove(e, evo.pair(p1, evo.ANY)) + assert(not evo.has(e, evo.pair(p1, s1))) + assert(evo.get(e, evo.pair(p1, s1)) == nil) + assert(not evo.has(e, evo.pair(p1, s2))) + assert(evo.get(e, evo.pair(p1, s2)) == nil) + assert(evo.has(e, evo.pair(p2, s1))) + assert(evo.get(e, evo.pair(p2, s1)) == 21) + assert(evo.has(e, evo.pair(p2, s2))) + assert(evo.get(e, evo.pair(p2, s2)) == 63) + end + + do + local e = evo.builder() + :set(evo.pair(p1, s1), 42) + :set(evo.pair(p1, s2), 84) + :set(evo.pair(p2, s1), 21) + :set(evo.pair(p2, s2), 63) + :spawn() + + evo.remove(e, evo.pair(evo.ANY, s2)) + assert(evo.has(e, evo.pair(p1, s1))) + assert(evo.get(e, evo.pair(p1, s1)) == 42) + assert(not evo.has(e, evo.pair(p1, s2))) + assert(evo.get(e, evo.pair(p1, s2)) == nil) + assert(evo.has(e, evo.pair(p2, s1))) + assert(evo.get(e, evo.pair(p2, s1)) == 21) + assert(not evo.has(e, evo.pair(p2, s2))) + assert(evo.get(e, evo.pair(p2, s2)) == nil) + end +end + +do + local p1, p2, s1, s2 = evo.id(4) + + ---@param o evolved.entity + ---@param s evolved.fragment + ---@return evolved.fragment[], evolved.component[], number + local function collect_primaries(o, s) + local fragments, components, count = {}, {}, 0 + + for f, c in evo.primaries(o, s) do + count = count + 1 + + fragments[count] = f + components[count] = c + + do + local ff, cc = evo.primary(o, s, count) + assert(ff == f and cc == c) + end + end + + assert(evo.primary_count(o, s) == count) + return fragments, components, count + end + + ---@param o evolved.entity + ---@param p evolved.fragment + ---@return evolved.fragment[], evolved.component[], number + local function collect_secondaries(o, p) + local fragments, components, count = {}, {}, 0 + + for f, c in evo.secondaries(o, p) do + count = count + 1 + fragments[count] = f + components[count] = c + end + + return fragments, components, count + end + + do + local e = evo.builder() + :set(evo.pair(p1, s1), 42) + :spawn() + + assert(evo.primary(e, s1) == p1) + assert(evo.primary(e, s2) == nil) + + assert(evo.secondary(e, p1) == s1) + assert(evo.secondary(e, p2) == nil) + + assert(evo.primary_count(e, s1) == 1) + assert(evo.primary_count(e, s2) == 0) + assert(evo.secondary_count(e, p1) == 1) + assert(evo.secondary_count(e, p2) == 0) + + do + local p_list, c_list, count = collect_primaries(e, s1) + assert(#p_list == 1 and #c_list == 1 and count == 1) + assert(p_list[1] == p1 and c_list[1] == 42) + end + + do + local p_list, c_list, count = collect_primaries(e, s2) + assert(#p_list == 0 and #c_list == 0 and count == 0) + end + + do + local s_list, c_list, count = collect_secondaries(e, p1) + assert(#s_list == 1 and #c_list == 1 and count == 1) + assert(s_list[1] == s1 and c_list[1] == 42) + end + + do + local s_list, c_list, count = collect_secondaries(e, p2) + assert(#s_list == 0 and #c_list == 0 and count == 0) + end + end + + do + local e = evo.builder() + :set(evo.pair(p1, s1), 42) + :set(evo.pair(p1, s2), 84) + :set(evo.pair(p2, s1), 21) + :set(evo.pair(p2, s2), 63) + :spawn() + + do + assert(evo.primary_count(e, s1) == 2) + assert(evo.primary_count(e, s2) == 2) + assert(evo.secondary_count(e, p1) == 2) + assert(evo.secondary_count(e, p2) == 2) + end + + do + local pp, cc = evo.primary(e, s1) + assert(pp == p1 and cc == 42) + + pp, cc = evo.primary(e, s1, 1) + assert(pp == p1 and cc == 42) + + pp, cc = evo.primary(e, s1, 2) + assert(pp == p2 and cc == 21) + + pp, cc = evo.primary(e, s1, 3) + assert(pp == nil and cc == nil) + end + + do + local pp, cc = evo.primary(e, s2) + assert(pp == p1 and cc == 84) + + pp, cc = evo.primary(e, s2, 1) + assert(pp == p1 and cc == 84) + + pp, cc = evo.primary(e, s2, 2) + assert(pp == p2 and cc == 63) + + pp, cc = evo.primary(e, s2, 3) + assert(pp == nil and cc == nil) + end + + do + local pp, cc = evo.secondary(e, p1) + assert(pp == s1 and cc == 42) + + pp, cc = evo.secondary(e, p1, 1) + assert(pp == s1 and cc == 42) + + pp, cc = evo.secondary(e, p1, 2) + assert(pp == s2 and cc == 84) + + pp, cc = evo.secondary(e, p1, 3) + assert(pp == nil and cc == nil) + end + + do + local pp, cc = evo.secondary(e, p2) + assert(pp == s1 and cc == 21) + + pp, cc = evo.secondary(e, p2, 1) + assert(pp == s1 and cc == 21) + + pp, cc = evo.secondary(e, p2, 2) + assert(pp == s2 and cc == 63) + + pp, cc = evo.secondary(e, p2, 3) + assert(pp == nil and cc == nil) + end + + do + local p_list, c_list, count = collect_primaries(e, s1) + assert(#p_list == 2 and #c_list == 2 and count == 2) + assert(p_list[1] == p1 and c_list[1] == 42) + assert(p_list[2] == p2 and c_list[2] == 21) + end + + do + local p_list, c_list, count = collect_primaries(e, s2) + assert(#p_list == 2 and #c_list == 2 and count == 2) + assert(p_list[1] == p1 and c_list[1] == 84) + assert(p_list[2] == p2 and c_list[2] == 63) + end + + do + local s_list, c_list, count = collect_secondaries(e, p1) + assert(#s_list == 2 and #c_list == 2 and count == 2) + assert(s_list[1] == s1 and c_list[1] == 42) + assert(s_list[2] == s2 and c_list[2] == 84) + end + + do + local s_list, c_list, count = collect_secondaries(e, p2) + assert(#s_list == 2 and #c_list == 2 and count == 2) + assert(s_list[1] == s1 and c_list[1] == 21) + assert(s_list[2] == s2 and c_list[2] == 63) + end + end +end + +do + local p, s = evo.id(2) + + local e = evo.id() + + assert(not evo.primary(e, s)) + assert(not evo.primary(e, s, 1)) + assert(not evo.primary(e, s, 2)) + assert(not evo.primary(e, s, 0)) + assert(not evo.primary(e, s, -1)) + assert(not evo.primary(e, s, -2)) + + assert(not evo.secondary(e, p)) + assert(not evo.secondary(e, p, 1)) + assert(not evo.secondary(e, p, 2)) + assert(not evo.secondary(e, p, 0)) + assert(not evo.secondary(e, p, -1)) + assert(not evo.secondary(e, p, -2)) + + assert(evo.primary_count(e, s) == 0) + assert(evo.secondary_count(e, p) == 0) + + assert(evo.primaries(e, s)() == nil) + assert(evo.secondaries(e, p)() == nil) +end + +do + local p1, p2, s1, s2 = evo.id(4) + + local e = evo.builder() + :set(evo.pair(p1, s1), 42) + :set(evo.pair(p1, s2), 84) + :set(evo.pair(p2, s1), 21) + :set(evo.pair(p2, s2), 63) + :spawn() + + assert(evo.primary(e, evo.ANY) == nil) + assert(evo.primary(e, evo.ANY, 1) == nil) + assert(evo.primary(e, evo.ANY, 2) == nil) + + assert(evo.secondary(e, evo.ANY) == nil) + assert(evo.secondary(e, evo.ANY, 1) == nil) + assert(evo.secondary(e, evo.ANY, 2) == nil) + + assert(evo.primaries(e, evo.ANY)() == nil) + assert(evo.secondaries(e, evo.ANY)() == nil) + + assert(evo.primary_count(e, evo.ANY) == 0) + assert(evo.secondary_count(e, evo.ANY) == 0) +end + +do + do + local p, s = evo.id(2) + + local e = evo.builder() + :set(s) + :set(evo.pair(p, s), 42) + :spawn() + + evo.destroy(p) + assert(evo.alive(e)) + assert(evo.has(e, s)) + assert(not evo.has(e, evo.pair(p, s))) + assert(evo.get(e, evo.pair(p, s)) == nil) + end + + do + local p, s = evo.id(2) + + local e = evo.builder() + :set(p) + :set(evo.pair(p, s), 42) + :spawn() + + evo.destroy(s) + assert(evo.alive(e)) + assert(evo.has(e, p)) + assert(not evo.has(e, evo.pair(p, s))) + assert(evo.get(e, evo.pair(p, s)) == nil) + end + + do + local p, s = evo.id(2) + evo.set(p, evo.DESTRUCTION_POLICY, evo.DESTRUCTION_POLICY_DESTROY_ENTITY) + + local e = evo.builder() + :set(evo.pair(p, s), 42) + :spawn() + + evo.destroy(p) + assert(not evo.alive(e)) + end + + do + local p, s = evo.id(2) + evo.set(s, evo.DESTRUCTION_POLICY, evo.DESTRUCTION_POLICY_DESTROY_ENTITY) + + local e = evo.builder() + :set(evo.pair(p, s), 42) + :spawn() + + evo.destroy(s) + assert(not evo.alive(e)) + end +end + +do + local p1, s1 = evo.id(2) + + local e0 = evo.builder() + :destruction_policy(evo.DESTRUCTION_POLICY_DESTROY_ENTITY) + :spawn() + + local e1 = evo.builder() + :set(evo.pair(p1, e0), 11) + :destruction_policy(evo.DESTRUCTION_POLICY_DESTROY_ENTITY) + :spawn() + + local e2 = evo.builder() + :set(evo.pair(e1, s1), 22) + :destruction_policy(evo.DESTRUCTION_POLICY_DESTROY_ENTITY) + :spawn() + + local e3 = evo.builder() + :set(evo.pair(e2, e2), 33) + :spawn() + + evo.destroy(e0) + assert(not evo.alive(e1)) + assert(not evo.alive(e2)) + assert(not evo.alive(e3)) +end + +do + do + local f, p, s = evo.id(3) + + local e = evo.builder():set(f, 21):set(evo.pair(p, s), 42):spawn() + + evo.destroy(f) + assert(evo.get(e, f) == nil) + assert(evo.get(e, evo.pair(p, s)) == 42) + end + + do + local f, p, s = evo.id(3) + + local e = evo.builder():set(f, 21):set(evo.pair(p, s), 42):spawn() + + evo.destroy(p) + assert(evo.get(e, f) == 21) + assert(evo.get(e, evo.pair(p, s)) == nil) + end + + do + local f, p, s = evo.id(3) + + local e = evo.builder():set(f, 21):set(evo.pair(p, s), 42):spawn() + + evo.destroy(s) + assert(evo.get(e, f) == 21) + assert(evo.get(e, evo.pair(p, s)) == nil) + end + + do + local f, p, s = evo.id(3) + + local e = evo.builder():set(f, 21):set(evo.pair(p, s), 42):spawn() + + evo.destroy(p, s) + assert(evo.get(e, f) == 21) + assert(evo.get(e, evo.pair(p, s)) == nil) + end + + do + local f, p, s = evo.id(3) + + local e = evo.builder():set(f, 21):set(evo.pair(p, s), 42):spawn() + + evo.destroy(f, p, s) + assert(evo.alive(e) and evo.empty(e)) + end +end + +do + do + local f, p, s = evo.id(3) + evo.set(p, evo.DESTRUCTION_POLICY, evo.DESTRUCTION_POLICY_DESTROY_ENTITY) + + local e = evo.builder():set(f, 21):set(evo.pair(p, s), 42):spawn() + + evo.destroy(p) + assert(not evo.alive(e)) + end + + do + local f, p, s = evo.id(3) + evo.set(s, evo.DESTRUCTION_POLICY, evo.DESTRUCTION_POLICY_DESTROY_ENTITY) + + local e = evo.builder():set(f, 21):set(evo.pair(p, s), 42):spawn() + + evo.destroy(p) + assert(evo.get(e, f) == 21) + assert(evo.get(e, evo.pair(p, s)) == nil) + end + + do + local f, p, s = evo.id(3) + evo.set(p, evo.DESTRUCTION_POLICY, evo.DESTRUCTION_POLICY_DESTROY_ENTITY) + + local e = evo.builder():set(f, 21):set(evo.pair(p, s), 42):spawn() + + evo.destroy(s) + assert(evo.get(e, f) == 21) + assert(evo.get(e, evo.pair(p, s)) == nil) + end + + do + local f, p, s = evo.id(3) + evo.set(s, evo.DESTRUCTION_POLICY, evo.DESTRUCTION_POLICY_DESTROY_ENTITY) + + local e = evo.builder():set(f, 21):set(evo.pair(p, s), 42):spawn() + + evo.destroy(s) + assert(not evo.alive(e)) + end +end + +do + local f, p, s = evo.id(3) + + local e = evo.builder():set(f, 21):set(evo.pair(p, s), 42):spawn() + + evo.destroy(evo.pair(p, s)) + evo.destroy(evo.pair(evo.ANY, s)) + evo.destroy(evo.pair(p, evo.ANY)) + evo.destroy(evo.pair(evo.ANY, evo.ANY)) + + assert(evo.get(e, f) == 21) + assert(evo.get(e, evo.pair(p, s)) == 42) +end + +do + evo.collect_garbage() + + local f, p1, s1, p2, s2 = evo.id(5) + + local e1 = evo.builder() + :set(f, 21) + :set(evo.pair(p1, s1), 42) + :set(evo.pair(p2, s2), 84) + :spawn() + + local e2 = evo.builder() + :set(f, 21) + :set(evo.pair(p1, s1), 42) + :set(evo.pair(p2, s2), 84) + :spawn() + + local f_chunk = evo.chunk(f) + local f_p2s2_chunk = evo.chunk(f, evo.pair(p2, s2)) + local f_p1s1_p2s2_chunk = evo.chunk(f, evo.pair(p1, s1), evo.pair(p2, s2)) + + assert(f_p1s1_p2s2_chunk:entities()[1] == e1) + assert(f_p1s1_p2s2_chunk:entities()[2] == e2) + + evo.remove(e1, evo.pair(p1, evo.ANY)) + + assert(f_p2s2_chunk:entities()[1] == e1) + assert(f_p1s1_p2s2_chunk:entities()[1] == e2) + + evo.remove(e1, evo.pair(p2, evo.ANY)) + + assert(f_chunk:entities()[1] == e1) + assert(f_p1s1_p2s2_chunk:entities()[1] == e2) + + evo.collect_garbage() + + assert(f_chunk:alive()) + assert(not f_p2s2_chunk:alive()) + assert(f_p1s1_p2s2_chunk:alive()) + + evo.remove(e2, evo.pair(p1, evo.ANY)) + + local new_f_p2s2_chunk = evo.chunk(f, evo.pair(p2, s2)) + assert(new_f_p2s2_chunk:entities()[1] == e2) +end + +do + evo.collect_garbage() + + local f, p1, p2, s1, s2 = evo.id(5) + + local e1 = evo.builder() + :set(f, 21) + :set(evo.pair(p1, s1), 42) + :set(evo.pair(p2, s2), 84) + :spawn() + + local f_p1s1_p2s2_chunk = evo.chunk(f, evo.pair(p1, s1), evo.pair(p2, s2)) + assert(f_p1s1_p2s2_chunk:entities()[1] == e1) + + evo.destroy(p2, s2) + + evo.collect_garbage() + + local f_p1s1_chunk = evo.chunk(f, evo.pair(p1, s1)) + assert(f_p1s1_chunk:entities()[1] == e1) +end + +do + local f, p, s = evo.id(3) + evo.set(p, evo.DEFAULT, 42) + + do + local e = evo.id() + evo.set(e, f) + evo.set(e, evo.pair(p, s)) + assert(evo.has(e, f) and evo.get(e, f) == true) + assert(evo.has(e, evo.pair(p, s)) and evo.get(e, evo.pair(p, s)) == 42) + end + + do + local e = evo.builder():set(f):set(evo.pair(p, s)):spawn() + assert(evo.has(e, f) and evo.get(e, f) == true) + assert(evo.has(e, evo.pair(p, s)) and evo.get(e, evo.pair(p, s)) == 42) + end + + do + local e = evo.builder():set(f, 84):set(evo.pair(p, s), 21):spawn() + evo.set(e, f) + evo.set(e, evo.pair(p, s)) + assert(evo.has(e, f) and evo.get(e, f) == true) + assert(evo.has(e, evo.pair(p, s)) and evo.get(e, evo.pair(p, s)) == 42) + end +end + +do + do + local f, p, s = evo.id(3) + assert(evo.empty(evo.pair(p, s))) + + evo.set(p, f) + assert(not evo.empty(evo.pair(p, s))) + + evo.destroy(p) + assert(evo.empty(evo.pair(p, s))) + end + + do + local f, p, s = evo.id(3) + assert(evo.empty(evo.pair(p, s))) + + evo.set(p, f) + assert(not evo.empty(evo.pair(p, s))) + + evo.destroy(s) + assert(not evo.empty(evo.pair(p, s))) + + evo.destroy(p) + assert(evo.empty(evo.pair(p, s))) + end + + do + local f, p, s = evo.id(3) + assert(not evo.has(p, f)) + assert(not evo.has(evo.pair(p, s), f)) + + evo.set(p, f, 42) + assert(evo.has(p, f)) + assert(evo.has(evo.pair(p, s), f)) + assert(not evo.has(evo.pair(s, p), f)) + assert(evo.get(p, f) == 42) + assert(evo.get(evo.pair(p, s), f) == 42) + assert(evo.get(evo.pair(s, p), f) == nil) + end +end + +do + local p, s = evo.id(3) + + local set_count = 0 + local insert_count = 0 + local remove_count = 0 + + evo.set(p, evo.ON_SET, function(e, f, nc, oc) + set_count = set_count + 1 + assert(f == p or f == evo.pair(p, s)) + assert(nc == 21 or nc == 42) + assert(oc == nil or oc == 21) + assert(evo.has(e, f)) + assert(evo.get(e, f) == nc) + end) + + evo.set(p, evo.ON_INSERT, function(e, f, nc) + insert_count = insert_count + 1 + assert(f == p or f == evo.pair(p, s)) + assert(nc == 21 or nc == 42) + assert(evo.has(e, f)) + assert(evo.get(e, f) == nc) + end) + + evo.set(p, evo.ON_REMOVE, function(e, f, oc) + remove_count = remove_count + 1 + assert(f == p or f == evo.pair(p, s)) + assert(oc == 21 or oc == 42) + assert(not evo.has(e, f)) + end) + + do + set_count, insert_count, remove_count = 0, 0, 0 + local e = evo.id() + evo.set(e, p, 21) + evo.set(e, evo.pair(p, s), 42) + assert(set_count == 2) + assert(insert_count == 2) + assert(remove_count == 0) + evo.remove(e, p) + assert(set_count == 2) + assert(insert_count == 2) + assert(remove_count == 1) + evo.remove(e, evo.pair(p, s)) + assert(set_count == 2) + assert(insert_count == 2) + assert(remove_count == 2) + end + + do + set_count, insert_count, remove_count = 0, 0, 0 + local e = evo.id() + evo.set(e, p, 21) + evo.set(e, evo.pair(p, s), 42) + assert(set_count == 2) + assert(insert_count == 2) + assert(remove_count == 0) + evo.destroy(e) + assert(set_count == 2) + assert(insert_count == 2) + assert(remove_count == 2) + end +end + +do + do + local f, p, s = evo.id(3) + evo.set(p, evo.DESTRUCTION_POLICY, evo.DESTRUCTION_POLICY_DESTROY_ENTITY) + + local e = evo.builder() + :set(f, 21) + :set(evo.pair(p, s), 42) + :spawn() + + evo.destroy(p) + + assert(not evo.alive(e) and evo.empty(e)) + assert(not evo.has(e, f) and evo.get(e, f) == nil) + assert(not evo.has(e, evo.pair(p, s)) and evo.get(e, evo.pair(p, s)) == nil) + end + + do + local f, p, s = evo.id(3) + evo.set(p, evo.DESTRUCTION_POLICY, evo.DESTRUCTION_POLICY_DESTROY_ENTITY) + + local e = evo.builder() + :set(f, 21) + :set(evo.pair(p, s), 42) + :spawn() + + evo.destroy(s) + + assert(evo.alive(e) and not evo.empty(e)) + assert(evo.has(e, f) and evo.get(e, f) == 21) + assert(not evo.has(e, evo.pair(p, s)) and evo.get(e, evo.pair(p, s)) == nil) + end + + do + local f, p, s = evo.id(3) + evo.set(p, evo.DESTRUCTION_POLICY, evo.DESTRUCTION_POLICY_REMOVE_FRAGMENT) + + local e = evo.builder() + :set(f, 21) + :set(evo.pair(p, s), 42) + :spawn() + + evo.destroy(p) + + assert(evo.alive(e) and not evo.empty(e)) + assert(evo.has(e, f) and evo.get(e, f) == 21) + assert(not evo.has(e, evo.pair(p, s)) and evo.get(e, evo.pair(p, s)) == nil) + end + + do + local f, p, s = evo.id(3) + evo.set(p, evo.DESTRUCTION_POLICY, evo.DESTRUCTION_POLICY_REMOVE_FRAGMENT) + + local e = evo.builder() + :set(f, 21) + :set(evo.pair(p, s), 42) + :spawn() + + evo.destroy(s) + + assert(evo.alive(e) and not evo.empty(e)) + assert(evo.has(e, f) and evo.get(e, f) == 21) + assert(not evo.has(e, evo.pair(p, s)) and evo.get(e, evo.pair(p, s)) == nil) + end + + do + local f, p, s = evo.id(3) + evo.set(p, evo.DESTRUCTION_POLICY, evo.DESTRUCTION_POLICY_REMOVE_FRAGMENT) + evo.set(s, evo.DESTRUCTION_POLICY, evo.DESTRUCTION_POLICY_DESTROY_ENTITY) + + local e = evo.builder() + :set(f, 21) + :set(evo.pair(p, s), 42) + :spawn() + + evo.destroy(p) + + assert(evo.alive(e) and not evo.empty(e)) + assert(evo.has(e, f) and evo.get(e, f) == 21) + assert(not evo.has(e, evo.pair(p, s)) and evo.get(e, evo.pair(p, s)) == nil) + end + + do + local f, p, s = evo.id(3) + evo.set(p, evo.DESTRUCTION_POLICY, evo.DESTRUCTION_POLICY_REMOVE_FRAGMENT) + evo.set(s, evo.DESTRUCTION_POLICY, evo.DESTRUCTION_POLICY_DESTROY_ENTITY) + + local e = evo.builder() + :set(f, 21) + :set(evo.pair(p, s), 42) + :spawn() + + evo.destroy(s) + + assert(not evo.alive(e) and evo.empty(e)) + assert(not evo.has(e, f) and evo.get(e, f) == nil) + assert(not evo.has(e, evo.pair(p, s)) and evo.get(e, evo.pair(p, s)) == nil) + end +end + +do + do + local p, s = evo.id(2) + evo.set(p, evo.DEFAULT, 42) + + do + local e = evo.id() + evo.set(e, evo.pair(p, s)) + assert(evo.has(e, evo.pair(p, s)) and evo.get(e, evo.pair(p, s)) == 42) + end + + do + local e = evo.builder():set(evo.pair(p, s)):spawn() + assert(evo.has(e, evo.pair(p, s)) and evo.get(e, evo.pair(p, s)) == 42) + end + end + + do + local p, s = evo.id(2) + evo.set(s, evo.DEFAULT, 21) + + do + local e = evo.id() + evo.set(e, evo.pair(p, s)) + assert(evo.has(e, evo.pair(p, s)) and evo.get(e, evo.pair(p, s)) == true) + end + + do + local e = evo.builder():set(evo.pair(p, s)):spawn() + assert(evo.has(e, evo.pair(p, s)) and evo.get(e, evo.pair(p, s)) == true) + end + end + + do + local p, s = evo.id(2) + evo.set(p, evo.DEFAULT, 42) + evo.set(s, evo.DEFAULT, 21) + + do + local e = evo.id() + evo.set(e, evo.pair(p, s)) + assert(evo.has(e, evo.pair(p, s)) and evo.get(e, evo.pair(p, s)) == 42) + end + + do + local e = evo.builder():set(evo.pair(p, s)):spawn() + assert(evo.has(e, evo.pair(p, s)) and evo.get(e, evo.pair(p, s)) == 42) + end + end +end + +do + do + local f, p, s = evo.id(3) + evo.set(p, evo.REQUIRES, { f }) + + do + local e = evo.id() + evo.set(e, evo.pair(p, s)) + assert(evo.has(e, f) and evo.get(e, f) == true) + assert(evo.has(e, evo.pair(p, s)) and evo.get(e, evo.pair(p, s)) == true) + end + + do + local e = evo.builder():set(evo.pair(p, s)):spawn() + assert(evo.has(e, f) and evo.get(e, f) == true) + assert(evo.has(e, evo.pair(p, s)) and evo.get(e, evo.pair(p, s)) == true) + end + end + + do + local f, p, s = evo.id(3) + evo.set(s, evo.REQUIRES, { f }) + + do + local e = evo.id() + evo.set(e, evo.pair(p, s)) + assert(not evo.has(e, f) and evo.get(e, f) == nil) + assert(evo.has(e, evo.pair(p, s)) and evo.get(e, evo.pair(p, s)) == true) + end + + do + local e = evo.builder():set(evo.pair(p, s)):spawn() + assert(not evo.has(e, f) and evo.get(e, f) == nil) + assert(evo.has(e, evo.pair(p, s)) and evo.get(e, evo.pair(p, s)) == true) + end + end + + do + local f, p, s = evo.id(3) + evo.set(p, evo.REQUIRES, { f }) + evo.set(f, evo.REQUIRES, { evo.pair(s, p) }) + evo.set(s, evo.REQUIRES, { p }) + + do + local e = evo.id() + evo.set(e, evo.pair(p, s)) + assert(evo.has(e, f) and evo.get(e, f) == true) + assert(evo.has(e, evo.pair(p, s)) and evo.get(e, evo.pair(p, s)) == true) + assert(evo.has(e, evo.pair(s, p)) and evo.get(e, evo.pair(s, p)) == true) + assert(evo.has(e, p) and evo.get(e, p) == true) + end + + do + local e = evo.builder():set(evo.pair(p, s)):spawn() + assert(evo.has(e, f) and evo.get(e, f) == true) + assert(evo.has(e, evo.pair(p, s)) and evo.get(e, evo.pair(p, s)) == true) + assert(evo.has(e, evo.pair(s, p)) and evo.get(e, evo.pair(s, p)) == true) + assert(evo.has(e, p) and evo.get(e, p) == true) + end + end +end + +do + do + local p, s = evo.id(2) + + local e = evo.builder():set(p, 21):set(evo.pair(p, s), 42):spawn() + assert(evo.has(e, p) and evo.get(e, p) == 21) + assert(evo.has(e, evo.pair(p, s)) and evo.get(e, evo.pair(p, s)) == 42) + + evo.set(s, evo.TAG) + assert(evo.has(e, p) and evo.get(e, p) == 21) + assert(evo.has(e, evo.pair(p, s)) and evo.get(e, evo.pair(p, s)) == 42) + + evo.set(p, evo.TAG) + assert(evo.has(e, p) and evo.get(e, p) == nil) + assert(evo.has(e, evo.pair(p, s)) and evo.get(e, evo.pair(p, s)) == nil) + end + + do + local p, s = evo.id(2) + + local e = evo.builder():set(evo.pair(p, s), 42):spawn() + assert(evo.has(e, evo.pair(p, s)) and evo.get(e, evo.pair(p, s)) == 42) + + evo.set(s, evo.TAG) + assert(evo.has(e, evo.pair(p, s)) and evo.get(e, evo.pair(p, s)) == 42) + + evo.set(p, evo.TAG) + assert(evo.has(e, evo.pair(p, s)) and evo.get(e, evo.pair(p, s)) == nil) + end +end + +do + local p1, p2, s1, s2 = evo.id(4) + + do + local b = evo.builder() + + b:set(evo.pair(p1, s1), 11) + b:set(evo.pair(p1, s2), 12) + b:set(evo.pair(p2, s1), 21) + b:set(evo.pair(p2, s2), 22) + + b:remove(evo.pair(evo.ANY, evo.ANY)) + + assert(not b:has(evo.pair(p1, s1))) + assert(not b:has(evo.pair(p1, s2))) + assert(not b:has(evo.pair(p2, s1))) + assert(not b:has(evo.pair(p2, s2))) + + assert(not b:has(evo.pair(p1, evo.ANY))) + assert(not b:has(evo.pair(p2, evo.ANY))) + assert(not b:has(evo.pair(evo.ANY, s1))) + assert(not b:has(evo.pair(evo.ANY, s2))) + + assert(not b:has(evo.pair(evo.ANY, evo.ANY))) + end + + do + local b = evo.builder() + + b:set(evo.pair(p1, s1), 11) + b:set(evo.pair(p1, s2), 12) + b:set(evo.pair(p2, s1), 21) + b:set(evo.pair(p2, s2), 22) + + b:remove(evo.pair(p2, evo.ANY)) + + assert(b:has(evo.pair(p1, s1))) + assert(b:has(evo.pair(p1, s2))) + assert(not b:has(evo.pair(p2, s1))) + assert(not b:has(evo.pair(p2, s2))) + + assert(b:has(evo.pair(p1, evo.ANY))) + assert(not b:has(evo.pair(p2, evo.ANY))) + assert(b:has(evo.pair(evo.ANY, s1))) + assert(b:has(evo.pair(evo.ANY, s2))) + + assert(b:has(evo.pair(evo.ANY, evo.ANY))) + end + + do + local b = evo.builder() + + b:set(evo.pair(p1, s1), 11) + b:set(evo.pair(p1, s2), 12) + b:set(evo.pair(p2, s1), 21) + b:set(evo.pair(p2, s2), 22) + + b:remove(evo.pair(p2, evo.ANY)) + + assert(b:has_all(evo.pair(p1, s1))) + assert(b:has_all(evo.pair(p1, s1), evo.pair(p1, s2))) + assert(not b:has_all(evo.pair(p1, s1), evo.pair(p2, s1))) + assert(not b:has_all(evo.pair(p2, s1), evo.pair(p1, s2))) + assert(not b:has_all(evo.pair(p2, s1), evo.pair(p2, s2))) + + assert(b:has_all(evo.pair(p1, evo.ANY))) + assert(b:has_all(evo.pair(p1, evo.ANY), evo.pair(evo.ANY, s1))) + assert(not b:has_all(evo.pair(p2, evo.ANY), evo.pair(evo.ANY, s1))) + assert(not b:has_all(evo.pair(p2, evo.ANY), evo.pair(evo.ANY, p1))) + + assert(b:has_all(evo.pair(evo.ANY, evo.ANY))) + + assert(b:has_all( + evo.pair(p1, s1), + evo.pair(p1, s2), + evo.pair(evo.ANY, s1), + evo.pair(p1, evo.ANY), + evo.pair(evo.ANY, evo.ANY))) + + assert(not b:has_all( + evo.pair(p1, s1), + evo.pair(p1, s2), + evo.pair(evo.ANY, s1), + evo.pair(p1, evo.ANY), + evo.pair(evo.ANY, evo.ANY), + evo.pair(p2, evo.ANY))) + end + + do + local b = evo.builder() + + b:set(evo.pair(p1, s1), 11) + b:set(evo.pair(p1, s2), 12) + b:set(evo.pair(p2, s1), 21) + b:set(evo.pair(p2, s2), 22) + + b:remove(evo.pair(p2, evo.ANY)) + + assert(b:has_any(evo.pair(p1, s1))) + assert(b:has_any(evo.pair(p1, s1), evo.pair(p1, s2))) + assert(b:has_any(evo.pair(p1, s1), evo.pair(p2, s1))) + assert(b:has_any(evo.pair(p2, s1), evo.pair(p1, s2))) + assert(not b:has_any(evo.pair(p2, s1), evo.pair(p2, s2))) + + assert(b:has_any(evo.pair(p1, evo.ANY))) + assert(b:has_any(evo.pair(p1, evo.ANY), evo.pair(evo.ANY, s1))) + assert(b:has_any(evo.pair(p2, evo.ANY), evo.pair(evo.ANY, s1))) + assert(not b:has_any(evo.pair(p2, evo.ANY), evo.pair(evo.ANY, p1))) + + assert(b:has_any(evo.pair(evo.ANY, evo.ANY))) + + assert(b:has_any( + evo.pair(p1, s1), + evo.pair(p1, s2), + evo.pair(evo.ANY, s1), + evo.pair(p1, evo.ANY), + evo.pair(evo.ANY, evo.ANY))) + + assert(not b:has_any( + evo.pair(p2, s1), + evo.pair(p2, s2), + evo.pair(p2, evo.ANY), + evo.pair(evo.ANY, p1), + evo.pair(evo.ANY, p2))) + + assert(b:has_any( + evo.pair(p2, s1), + evo.pair(p2, s2), + evo.pair(p2, evo.ANY), + evo.pair(evo.ANY, p1), + evo.pair(evo.ANY, p2), + evo.pair(p1, evo.ANY))) + end + + do + local b = evo.builder() + + b:set(evo.pair(p1, s1), 11) + b:set(evo.pair(p1, s2), 12) + b:set(evo.pair(p2, s1), 21) + b:set(evo.pair(p2, s2), 22) + + b:remove(evo.pair(p1, evo.ANY)) + b:remove(evo.pair(p1, evo.ANY)) + + b:remove(evo.pair(p2, evo.ANY)) + b:remove(evo.pair(p2, evo.ANY)) + + b:remove(evo.pair(evo.ANY, s1)) + b:remove(evo.pair(evo.ANY, s1)) + + b:remove(evo.pair(evo.ANY, s2)) + b:remove(evo.pair(evo.ANY, s2)) + + assert(not b:has(evo.pair(evo.ANY, evo.ANY))) + end +end + +-- TODO +-- builder:has/has_all/has_any should work with wildcards / remove too? +-- should we provide wildcard support for get operations? +-- prevent setting pairs with dead secondary fragments +-- process evo.ANY as single wildcard diff --git a/evolved.lua b/evolved.lua index 416f0ea..a1fb6fe 100644 --- a/evolved.lua +++ b/evolved.lua @@ -82,6 +82,20 @@ local evolved = { ---@field package [3] integer chunk_stack_size ---@field package [4] table? exclude_set +---@class (exact) evolved.primaries_state +---@field package [1] integer structural_changes +---@field package [2] evolved.chunk entity_chunk +---@field package [3] integer entity_place +---@field package [4] integer secondary_index +---@field package [5] integer secondary_fragment_index + +---@class (exact) evolved.secondaries_state +---@field package [1] integer structural_changes +---@field package [2] evolved.chunk entity_chunk +---@field package [3] integer entity_place +---@field package [4] integer primary_index +---@field package [5] integer primary_fragment_index + ---@alias evolved.each_iterator fun( --- state: evolved.each_state?): --- evolved.fragment?, evolved.component? @@ -90,6 +104,14 @@ local evolved = { --- state: evolved.execute_state?): --- evolved.chunk?, evolved.entity[]?, integer? +---@alias evolved.primaries_iterator fun( +--- state: evolved.primaries_state?): +--- evolved.fragment?, evolved.component? + +---@alias evolved.secondaries_iterator fun( +--- state: evolved.secondaries_state?): +--- evolved.fragment?, evolved.component? + --- --- --- @@ -98,11 +120,15 @@ local evolved = { --[=[------------------------------------------------------------------\ | |-------- OPTIONS --------|- SECONDARY -|-- PRIMARY --| - | IDENTIFIER'S | | | | - | ANATOMY | 12 bits | 20 bits | 20 bits | - | | | | | - |--------------|-------------------------|-------------|-------------| - | ID | RESERVED | version | index | + | IDENTIFIER'S | 12 bits | | | + | ANATOMY |--------|--------|-------| 20 bits | 20 bits | + | | 9 bits | 2 bits | 1 bit | | | + |--------------|--------|--------|-------|-------------|-------------| + | ID | RSVD | 00 | 0 | version | index | + | PAIR | RSVD | 00 | 1 | SEC index | PRI index | + | PRI WILDCARD | RSVD | 01 | 1 | SEC index | ANY index | + | SEC WILDCARD | RSVD | 10 | 1 | ANY index | PRI index | + | ANY WILDCARD | RSVD | 11 | 1 | ANY index | ANY index | \------------------------------------------------------------------]=] --- @@ -159,6 +185,10 @@ local __group_subsystems = {} ---@type table ---@field package __component_indices table ---@field package __component_storages evolved.storage[] ---@field package __component_fragments evolved.fragment[] +---@field package __pair_list evolved.id[] +---@field package __pair_count integer +---@field package __primary_pairs table +---@field package __secondary_pairs table ---@field package __with_fragment_edges table ---@field package __without_fragment_edges table ---@field package __unreachable_or_collected boolean @@ -166,6 +196,9 @@ local __group_subsystems = {} ---@type table ---@field package __has_assign_hooks boolean ---@field package __has_insert_hooks boolean ---@field package __has_remove_hooks boolean +---@field package __has_pair_major boolean +---@field package __has_pair_minors boolean +---@field package __has_pair_fragments boolean ---@field package __has_unique_major boolean ---@field package __has_unique_minors boolean ---@field package __has_unique_fragments boolean @@ -181,6 +214,8 @@ __chunk_mt.__index = __chunk_mt ---@class evolved.builder ---@field package __components table +---@field package __primary_pairs? table +---@field package __secondary_pairs? table local __builder_mt = {} __builder_mt.__index = __builder_mt @@ -428,13 +463,15 @@ local __table_pool_tag = { system_list = 3, each_state = 4, execute_state = 5, - entity_set = 6, - entity_list = 7, - fragment_set = 8, - fragment_list = 9, - component_map = 10, - component_list = 11, - __count = 11, + primaries_state = 6, + secondaries_state = 7, + entity_set = 8, + entity_list = 9, + fragment_set = 10, + fragment_list = 11, + component_map = 12, + component_list = 13, + __count = 13, } ---@class (exact) evolved.table_pool @@ -542,6 +579,7 @@ end ---@field package __item_count integer local __assoc_list_new +local __assoc_list_dup local __assoc_list_move local __assoc_list_move_ex local __assoc_list_sort @@ -563,6 +601,31 @@ function __assoc_list_new(reserve) } end +---@generic K +---@param al evolved.assoc_list +---@return evolved.assoc_list +---@nodiscard +function __assoc_list_dup(al) + local al_item_list = al.__item_list + local al_item_count = al.__item_count + + local dup_item_set = __lua_table_new(0, al_item_count) + local dup_item_list = __lua_table_new(al_item_count, 0) + + for al_item_index = 1, al_item_count do + local al_item = al_item_list[al_item_index] + dup_item_set[al_item] = al_item_index + dup_item_list[al_item_index] = al_item + end + + ---@type evolved.assoc_list + return { + __item_set = dup_item_set, + __item_list = dup_item_list, + __item_count = al_item_count, + } +end + ---@generic K ---@param src_item_list K[] ---@param src_item_first integer @@ -712,6 +775,8 @@ end --- --- +local __ANY = __acquire_id() + local __TAG = __acquire_id() local __NAME = __acquire_id() @@ -752,6 +817,23 @@ local __DESTRUCTION_POLICY_REMOVE_FRAGMENT = __acquire_id() --- --- +local __PAIR_OPTS = 1 -- 0b001 +local __WILDCARD_OPTS = 7 -- 0b111 +local __PRIMARY_WILDCARD_OPTS = 3 -- 0b011 +local __SECONDARY_WILDCARD_OPTS = 5 -- 0b101 + +local __ANY_INDEX = __ANY % 2 ^ 20 --[[@as integer]] + +local __WILDCARD_PAIR = __ANY_INDEX + + __ANY_INDEX * 2 ^ 20 + + __WILDCARD_OPTS * 2 ^ 40 --[[@as evolved.id]] + +--- +--- +--- +--- +--- + local __safe_tbls = { ---@type table __EMPTY_FRAGMENT_SET = __lua_setmetatable({}, { @@ -834,6 +916,21 @@ local __evolved_process local __evolved_debug_mode local __evolved_collect_garbage +local __evolved_pair +local __evolved_unpair + +local __evolved_is_pair +local __evolved_is_wildcard + +local __evolved_primary +local __evolved_secondary + +local __evolved_primaries +local __evolved_secondaries + +local __evolved_primary_count +local __evolved_secondary_count + local __evolved_chunk local __evolved_builder @@ -845,23 +942,79 @@ local __evolved_builder local __id_name +local __primary_wildcard +local __secondary_wildcard + local __component_storage ---@param id evolved.id ---@return string ---@nodiscard function __id_name(id) - ---@type string? - local id_name = __evolved_get(id, __NAME) + if not __evolved_is_pair(id) then + ---@type string? + local id_name = __evolved_get(id, __NAME) - if id_name then - return id_name + if id_name then + return id_name + end + else + local id_primary_index, id_secondary_index = __evolved_unpack(id) + + local id_primary = __freelist_ids[id_primary_index] --[[@as evolved.id?]] + local id_secondary = __freelist_ids[id_secondary_index] --[[@as evolved.id?]] + + local id_primary_name, id_secondary_name + + if id_primary and id_primary % 2 ^ 20 == id_primary_index then + id_primary_name = __id_name(id_primary) + end + + if id_secondary and id_secondary % 2 ^ 20 == id_secondary_index then + id_secondary_name = __id_name(id_secondary) + end + + if id_primary_name and id_secondary_name then + return __lua_string_format('${%s,%s}', id_primary_name, id_secondary_name) + end end local id_primary, id_secondary, id_options = __evolved_unpack(id) return __lua_string_format('$%d#%d:%d:%d', id, id_primary, id_secondary, id_options) end +---@param secondary evolved.id | integer id or index +---@return evolved.id pair (*, secondary) +---@nodiscard +function __primary_wildcard(secondary) + local primary_index = __ANY_INDEX + local secondary_index = secondary % 2 ^ 20 + + if secondary_index == __ANY_INDEX then + return __WILDCARD_PAIR + end + + return primary_index + + secondary_index * 2 ^ 20 + + __PRIMARY_WILDCARD_OPTS * 2 ^ 40 --[[@as evolved.id]] +end + +---@param primary evolved.id | integer id or index +---@return evolved.id pair (primary, *) +---@nodiscard +function __secondary_wildcard(primary) + local primary_index = primary % 2 ^ 20 + local secondary_index = __ANY_INDEX + + if primary_index == __ANY_INDEX then + return __WILDCARD_PAIR + end + + return primary_index + + secondary_index * 2 ^ 20 + + __SECONDARY_WILDCARD_OPTS * 2 ^ 40 --[[@as evolved.id]] +end + ---@param fragment evolved.fragment ---@return evolved.storage ---@nodiscard @@ -937,6 +1090,16 @@ function __iterator_fns.__execute_major_iterator(execute_state) (not chunk_child.__has_explicit_major) and (not exclude_set or not exclude_set[chunk_child_fragment]) + if is_chunk_child_matched and exclude_set and chunk_child.__has_pair_major then + local chunk_child_primary_index, chunk_child_secondary_index = + __evolved_unpack(chunk_child_fragment) + + is_chunk_child_matched = + not exclude_set[__WILDCARD_PAIR] and + not exclude_set[__primary_wildcard(chunk_child_secondary_index)] and + not exclude_set[__secondary_wildcard(chunk_child_primary_index)] + end + if is_chunk_child_matched then chunk_stack_size = chunk_stack_size + 1 chunk_stack[chunk_stack_size] = chunk_child @@ -987,6 +1150,72 @@ function __iterator_fns.__execute_minor_iterator(execute_state) __release_table(__table_pool_tag.execute_state, execute_state, true) end +---@type evolved.primaries_iterator +function __iterator_fns.__primaries_iterator(primaries_state) + if not primaries_state then return end + + local structural_changes = primaries_state[1] + local entity_chunk = primaries_state[2] + local entity_place = primaries_state[3] + local secondary_index = primaries_state[4] + local secondary_fragment_index = primaries_state[5] + + if structural_changes ~= __structural_changes then + __error_fmt('structural changes are prohibited during iteration') + end + + local secondary_fragments = entity_chunk.__secondary_pairs[secondary_index] + local secondary_fragment_list = secondary_fragments and secondary_fragments.__item_list + local secondary_fragment_count = secondary_fragments and secondary_fragments.__item_count or 0 + + if secondary_fragment_index >= 1 and secondary_fragment_index <= secondary_fragment_count then + primaries_state[5] = secondary_fragment_index + 1 + + local secondary_fragment = secondary_fragment_list[secondary_fragment_index] + local primary, _ = __evolved_unpair(secondary_fragment) + + local component_index = entity_chunk.__component_indices[secondary_fragment] + local component_storage = entity_chunk.__component_storages[component_index] + + return primary, component_storage and component_storage[entity_place] + end + + __release_table(__table_pool_tag.primaries_state, primaries_state, true) +end + +---@type evolved.secondaries_iterator +function __iterator_fns.__secondaries_iterator(secondaries_state) + if not secondaries_state then return end + + local structural_changes = secondaries_state[1] + local entity_chunk = secondaries_state[2] + local entity_place = secondaries_state[3] + local primary_index = secondaries_state[4] + local primary_fragment_index = secondaries_state[5] + + if structural_changes ~= __structural_changes then + __error_fmt('structural changes are prohibited during iteration') + end + + local primary_fragments = entity_chunk.__primary_pairs[primary_index] + local primary_fragment_list = primary_fragments and primary_fragments.__item_list + local primary_fragment_count = primary_fragments and primary_fragments.__item_count or 0 + + if primary_fragment_index >= 1 and primary_fragment_index <= primary_fragment_count then + secondaries_state[5] = primary_fragment_index + 1 + + local primary_fragment = primary_fragment_list[primary_fragment_index] + local _, secondary = __evolved_unpair(primary_fragment) + + local component_index = entity_chunk.__component_indices[primary_fragment] + local component_storage = entity_chunk.__component_storages[component_index] + + return secondary, component_storage and component_storage[entity_place] + end + + __release_table(__table_pool_tag.secondaries_state, secondaries_state, true) +end + --- --- --- @@ -1006,17 +1235,48 @@ local __update_major_chunks_trace ---@return evolved.chunk ---@nodiscard function __new_chunk(chunk_parent, chunk_fragment) + if __evolved_is_wildcard(chunk_fragment) then + __error_fmt('chunk cannot contain wildcard fragments') + end + local chunk_fragment_set = {} ---@type table local chunk_fragment_list = {} ---@type evolved.fragment[] local chunk_fragment_count = 0 ---@type integer - if chunk_parent then - local chunk_parent_fragment_list = chunk_parent.__fragment_list - local chunk_parent_fragment_count = chunk_parent.__fragment_count + local chunk_pair_list = {} ---@type evolved.id[] + local chunk_pair_count = 0 ---@type integer - chunk_fragment_count = __assoc_list_move_ex( - chunk_parent_fragment_list, 1, chunk_parent_fragment_count, - chunk_fragment_set, chunk_fragment_list, chunk_fragment_count) + local chunk_primary_pairs = {} ---@type table + local chunk_secondary_pairs = {} ---@type table + + if chunk_parent then + do + local chunk_parent_fragment_list = chunk_parent.__fragment_list + local chunk_parent_fragment_count = chunk_parent.__fragment_count + + chunk_fragment_count = __assoc_list_move_ex( + chunk_parent_fragment_list, 1, chunk_parent_fragment_count, + chunk_fragment_set, chunk_fragment_list, chunk_fragment_count) + end + + do + local chunk_parent_pair_list = chunk_parent.__pair_list + local chunk_parent_pair_count = chunk_parent.__pair_count + + __lua_table_move( + chunk_parent_pair_list, 1, chunk_parent_pair_count, + chunk_pair_count + 1, chunk_pair_list) + + chunk_pair_count = chunk_pair_count + chunk_parent_pair_count + end + + for parent_primary_index, parent_primary_fragments in __lua_next, chunk_parent.__primary_pairs do + chunk_primary_pairs[parent_primary_index] = __assoc_list_dup(parent_primary_fragments) + end + + for parent_secondary_index, parent_secondary_fragments in __lua_next, chunk_parent.__secondary_pairs do + chunk_secondary_pairs[parent_secondary_index] = __assoc_list_dup(parent_secondary_fragments) + end end do @@ -1025,6 +1285,30 @@ function __new_chunk(chunk_parent, chunk_fragment) chunk_fragment_list[chunk_fragment_count] = chunk_fragment end + if __evolved_is_pair(chunk_fragment) then + chunk_pair_count = chunk_pair_count + 1 + chunk_pair_list[chunk_pair_count] = chunk_fragment + + local chunk_primary_index, chunk_secondary_index = + __evolved_unpack(chunk_fragment) + + local chunk_primary_fragments = chunk_primary_pairs[chunk_primary_index] + local chunk_secondary_fragments = chunk_secondary_pairs[chunk_secondary_index] + + if not chunk_primary_fragments then + chunk_primary_fragments = __assoc_list_new(1) + chunk_primary_pairs[chunk_primary_index] = chunk_primary_fragments + end + + if not chunk_secondary_fragments then + chunk_secondary_fragments = __assoc_list_new(1) + chunk_secondary_pairs[chunk_secondary_index] = chunk_secondary_fragments + end + + __assoc_list_insert(chunk_primary_fragments, chunk_fragment) + __assoc_list_insert(chunk_secondary_fragments, chunk_fragment) + end + ---@type evolved.chunk local chunk = __lua_setmetatable({ __parent = nil, @@ -1041,6 +1325,10 @@ function __new_chunk(chunk_parent, chunk_fragment) __component_indices = {}, __component_storages = {}, __component_fragments = {}, + __pair_list = chunk_pair_list, + __pair_count = chunk_pair_count, + __primary_pairs = chunk_primary_pairs, + __secondary_pairs = chunk_secondary_pairs, __with_fragment_edges = {}, __without_fragment_edges = {}, __unreachable_or_collected = false, @@ -1048,6 +1336,9 @@ function __new_chunk(chunk_parent, chunk_fragment) __has_assign_hooks = false, __has_insert_hooks = false, __has_remove_hooks = false, + __has_pair_major = false, + __has_pair_minors = false, + __has_pair_fragments = false, __has_unique_major = false, __has_unique_minors = false, __has_unique_fragments = false, @@ -1103,6 +1394,90 @@ function __new_chunk(chunk_parent, chunk_fragment) __assoc_list_insert(minor_chunks, chunk) end + if __evolved_is_pair(chunk_fragment) then + local major = chunk_fragment + local major_primary_index, major_secondary_index = + __evolved_unpack(major) + + do + local major_wildcard = __WILDCARD_PAIR + local major_wildcard_chunks = __major_chunks[major_wildcard] + + if not major_wildcard_chunks then + major_wildcard_chunks = __assoc_list_new(4) + __major_chunks[major_wildcard] = major_wildcard_chunks + end + + __assoc_list_insert(major_wildcard_chunks, chunk) + end + + do + local major_wildcard = __secondary_wildcard(major_primary_index) + local major_wildcard_chunks = __major_chunks[major_wildcard] + + if not major_wildcard_chunks then + major_wildcard_chunks = __assoc_list_new(4) + __major_chunks[major_wildcard] = major_wildcard_chunks + end + + __assoc_list_insert(major_wildcard_chunks, chunk) + end + + do + local major_wildcard = __primary_wildcard(major_secondary_index) + local major_wildcard_chunks = __major_chunks[major_wildcard] + + if not major_wildcard_chunks then + major_wildcard_chunks = __assoc_list_new(4) + __major_chunks[major_wildcard] = major_wildcard_chunks + end + + __assoc_list_insert(major_wildcard_chunks, chunk) + end + end + + if chunk_pair_count > 0 then + local minor_wildcard = __WILDCARD_PAIR + local minor_wildcard_chunks = __minor_chunks[minor_wildcard] + + if not minor_wildcard_chunks then + minor_wildcard_chunks = __assoc_list_new(4) + __minor_chunks[minor_wildcard] = minor_wildcard_chunks + end + + __assoc_list_insert(minor_wildcard_chunks, chunk) + end + + for i = 1, chunk_pair_count do + local minor = chunk_pair_list[i] + local minor_primary_index, minor_secondary_index = + __evolved_unpack(minor) + + do + local minor_wildcard = __secondary_wildcard(minor_primary_index) + local minor_wildcard_chunks = __minor_chunks[minor_wildcard] + + if not minor_wildcard_chunks then + minor_wildcard_chunks = __assoc_list_new(4) + __minor_chunks[minor_wildcard] = minor_wildcard_chunks + end + + __assoc_list_insert(minor_wildcard_chunks, chunk) + end + + do + local minor_wildcard = __primary_wildcard(minor_secondary_index) + local minor_wildcard_chunks = __minor_chunks[minor_wildcard] + + if not minor_wildcard_chunks then + minor_wildcard_chunks = __assoc_list_new(4) + __minor_chunks[minor_wildcard] = minor_wildcard_chunks + end + + __assoc_list_insert(minor_wildcard_chunks, chunk) + end + end + __update_chunk_tags(chunk) __update_chunk_flags(chunk) @@ -1190,6 +1565,10 @@ function __update_chunk_flags(chunk) local has_remove_hooks = (chunk_parent ~= nil and chunk_parent.__has_remove_hooks) or __evolved_has(chunk_fragment, __ON_REMOVE) + local has_pair_major = __evolved_is_pair(chunk_fragment) + local has_pair_minors = chunk_parent ~= nil and chunk_parent.__has_pair_fragments + local has_pair_fragments = has_pair_major or has_pair_minors + local has_unique_major = __evolved_has(chunk_fragment, __UNIQUE) local has_unique_minors = chunk_parent ~= nil and chunk_parent.__has_unique_fragments local has_unique_fragments = has_unique_major or has_unique_minors @@ -1210,6 +1589,10 @@ function __update_chunk_flags(chunk) chunk.__has_insert_hooks = has_insert_hooks chunk.__has_remove_hooks = has_remove_hooks + chunk.__has_pair_major = has_pair_major + chunk.__has_pair_minors = has_pair_minors + chunk.__has_pair_fragments = has_pair_fragments + chunk.__has_unique_major = has_unique_major chunk.__has_unique_minors = has_unique_minors chunk.__has_unique_fragments = has_unique_fragments @@ -1229,6 +1612,10 @@ end ---@param trace fun(chunk: evolved.chunk, ...: any) ---@param ... any additional trace arguments function __trace_major_chunks(major, trace, ...) + if __evolved_is_pair(major) then + __error_fmt('trace operations on pair fragments are not supported') + end + ---@type evolved.chunk[] local chunk_stack = __acquire_table(__table_pool_tag.chunk_list) local chunk_stack_size = 0 @@ -1247,6 +1634,34 @@ function __trace_major_chunks(major, trace, ...) end end + do + local major_chunks = __major_chunks[__primary_wildcard(major)] + local major_chunk_list = major_chunks and major_chunks.__item_list --[=[@as evolved.chunk[]]=] + local major_chunk_count = major_chunks and major_chunks.__item_count or 0 --[[@as integer]] + + if major_chunk_count > 0 then + __lua_table_move( + major_chunk_list, 1, major_chunk_count, + chunk_stack_size + 1, chunk_stack) + + chunk_stack_size = chunk_stack_size + major_chunk_count + end + end + + do + local major_chunks = __major_chunks[__secondary_wildcard(major)] + local major_chunk_list = major_chunks and major_chunks.__item_list --[=[@as evolved.chunk[]]=] + local major_chunk_count = major_chunks and major_chunks.__item_count or 0 --[[@as integer]] + + if major_chunk_count > 0 then + __lua_table_move( + major_chunk_list, 1, major_chunk_count, + chunk_stack_size + 1, chunk_stack) + + chunk_stack_size = chunk_stack_size + major_chunk_count + end + end + while chunk_stack_size > 0 do local chunk = chunk_stack[chunk_stack_size] @@ -1274,6 +1689,10 @@ end ---@param trace fun(chunk: evolved.chunk, ...: any) ---@param ... any additional trace arguments function __trace_minor_chunks(minor, trace, ...) + if __evolved_is_pair(minor) then + __error_fmt('trace operations on pair fragments are not supported') + end + ---@type evolved.chunk[] local chunk_stack = __acquire_table(__table_pool_tag.chunk_list) local chunk_stack_size = 0 @@ -1292,6 +1711,34 @@ function __trace_minor_chunks(minor, trace, ...) end end + do + local minor_chunks = __minor_chunks[__primary_wildcard(minor)] + local minor_chunk_list = minor_chunks and minor_chunks.__item_list --[=[@as evolved.chunk[]]=] + local minor_chunk_count = minor_chunks and minor_chunks.__item_count or 0 --[[@as integer]] + + if minor_chunk_count > 0 then + __lua_table_move( + minor_chunk_list, 1, minor_chunk_count, + chunk_stack_size + 1, chunk_stack) + + chunk_stack_size = chunk_stack_size + minor_chunk_count + end + end + + do + local minor_chunks = __minor_chunks[__secondary_wildcard(minor)] + local minor_chunk_list = minor_chunks and minor_chunks.__item_list --[=[@as evolved.chunk[]]=] + local minor_chunk_count = minor_chunks and minor_chunks.__item_count or 0 --[[@as integer]] + + if minor_chunk_count > 0 then + __lua_table_move( + minor_chunk_list, 1, minor_chunk_count, + chunk_stack_size + 1, chunk_stack) + + chunk_stack_size = chunk_stack_size + minor_chunk_count + end + end + while chunk_stack_size > 0 do local chunk = chunk_stack[chunk_stack_size] @@ -1398,6 +1845,96 @@ local function __chunk_without_fragment(chunk, fragment) if without_fragment_edge then return without_fragment_edge end end + if chunk.__has_pair_fragments and __evolved_is_wildcard(fragment) then + local primary_index, secondary_index, fragment_opts = + __evolved_unpack(fragment) + + if fragment_opts == __WILDCARD_OPTS then + while chunk and chunk.__has_pair_major do + chunk = chunk.__parent + end + + if not chunk or not chunk.__has_pair_fragments then + return chunk + end + + local sib_chunk = chunk.__parent + + while sib_chunk and sib_chunk.__has_pair_fragments do + sib_chunk = sib_chunk.__parent + end + + if sib_chunk then + chunk.__without_fragment_edges[fragment] = sib_chunk + sib_chunk.__with_fragment_edges[fragment] = chunk + end + + return sib_chunk + elseif fragment_opts == __PRIMARY_WILDCARD_OPTS then + if not chunk.__secondary_pairs[secondary_index] then + -- the chunk does not have such pairs + return chunk + end + + local sib_chunk = chunk.__parent + + while sib_chunk and sib_chunk.__has_pair_fragments and sib_chunk.__secondary_pairs[secondary_index] do + sib_chunk = sib_chunk.__parent + end + + local ini_pair_list = chunk.__pair_list + local ini_pair_count = chunk.__pair_count + + local lst_pair_index = sib_chunk and sib_chunk.__pair_count + 2 or 2 + + for ini_pair_index = lst_pair_index, ini_pair_count do + local ini_pair = ini_pair_list[ini_pair_index] + local _, ini_secondary_index = __evolved_unpack(ini_pair) + if ini_secondary_index ~= secondary_index then + sib_chunk = __chunk_with_fragment(sib_chunk, ini_pair) + end + end + + if sib_chunk then + chunk.__without_fragment_edges[fragment] = sib_chunk + sib_chunk.__with_fragment_edges[fragment] = chunk + end + + return sib_chunk + elseif fragment_opts == __SECONDARY_WILDCARD_OPTS then + if not chunk.__primary_pairs[primary_index] then + -- the chunk does not have such pairs + return chunk + end + + local sib_chunk = chunk.__parent + + while sib_chunk and sib_chunk.__has_pair_fragments and sib_chunk.__primary_pairs[primary_index] do + sib_chunk = sib_chunk.__parent + end + + local ini_pair_list = chunk.__pair_list + local ini_pair_count = chunk.__pair_count + + local lst_pair_index = sib_chunk and sib_chunk.__pair_count + 2 or 2 + + for ini_pair_index = lst_pair_index, ini_pair_count do + local ini_pair = ini_pair_list[ini_pair_index] + local ini_primary_index, _ = __evolved_unpack(ini_pair) + if ini_primary_index ~= primary_index then + sib_chunk = __chunk_with_fragment(sib_chunk, ini_pair) + end + end + + if sib_chunk then + chunk.__without_fragment_edges[fragment] = sib_chunk + sib_chunk.__with_fragment_edges[fragment] = chunk + end + + return sib_chunk + end + end + if fragment > chunk.__fragment or not chunk.__fragment_set[fragment] then return chunk end @@ -1545,6 +2082,21 @@ local function __chunk_has_fragment(chunk, fragment) return true end + if chunk.__has_pair_fragments and __evolved_is_wildcard(fragment) then + local primary_index, secondary_index, fragment_opts = + __evolved_unpack(fragment) + + if fragment_opts == __WILDCARD_OPTS then + return true + elseif fragment_opts == __PRIMARY_WILDCARD_OPTS then + local secondary_fragments = chunk.__secondary_pairs[secondary_index] + return secondary_fragments and secondary_fragments.__item_count > 0 + elseif fragment_opts == __SECONDARY_WILDCARD_OPTS then + local primary_fragments = chunk.__primary_pairs[primary_index] + return primary_fragments and primary_fragments.__item_count > 0 + end + end + return false end @@ -1561,30 +2113,41 @@ local function __chunk_has_all_fragments(chunk, ...) local fs = chunk.__fragment_set + local has_f = __chunk_has_fragment + local has_fs = __chunk_has_all_fragments + + local has_p = chunk.__has_pair_fragments + if fragment_count == 1 then local f1 = ... - return fs[f1] ~= nil + return (has_p and has_f(chunk, f1)) + or (not has_p and fs[f1] ~= nil) end if fragment_count == 2 then local f1, f2 = ... - return fs[f1] ~= nil and fs[f2] ~= nil + return (has_p and has_f(chunk, f1) and has_f(chunk, f2)) + or (not has_p and fs[f1] ~= nil and fs[f2] ~= nil) end if fragment_count == 3 then local f1, f2, f3 = ... - return fs[f1] ~= nil and fs[f2] ~= nil and fs[f3] ~= nil + return (has_p and has_f(chunk, f1) and has_f(chunk, f2) and has_f(chunk, f3)) + or (not has_p and fs[f1] ~= nil and fs[f2] ~= nil and fs[f3] ~= nil) end if fragment_count == 4 then local f1, f2, f3, f4 = ... - return fs[f1] ~= nil and fs[f2] ~= nil and fs[f3] ~= nil and fs[f4] ~= nil + return (has_p and has_f(chunk, f1) and has_f(chunk, f2) and has_f(chunk, f3) and has_f(chunk, f4)) + or (not has_p and fs[f1] ~= nil and fs[f2] ~= nil and fs[f3] ~= nil and fs[f4] ~= nil) end do local f1, f2, f3, f4 = ... - return fs[f1] ~= nil and fs[f2] ~= nil and fs[f3] ~= nil and fs[f4] ~= nil - and __chunk_has_all_fragments(chunk, __lua_select(5, ...)) + return (has_p and has_f(chunk, f1) and has_f(chunk, f2) and has_f(chunk, f3) and has_f(chunk, f4) + and has_fs(chunk, __lua_select(5, ...))) + or (not has_p and fs[f1] ~= nil and fs[f2] ~= nil and fs[f3] ~= nil and fs[f4] ~= nil + and has_fs(chunk, __lua_select(5, ...))) end end @@ -1600,10 +2163,22 @@ local function __chunk_has_all_fragment_list(chunk, fragment_list, fragment_coun local fs = chunk.__fragment_set - for i = 1, fragment_count do - local f = fragment_list[i] - if fs[f] == nil then - return false + local has_f = __chunk_has_fragment + local has_p = chunk.__has_pair_fragments + + if has_p then + for i = 1, fragment_count do + local f = fragment_list[i] + if not has_f(chunk, f) then + return false + end + end + else + for i = 1, fragment_count do + local f = fragment_list[i] + if fs[f] == nil then + return false + end end end @@ -1623,30 +2198,41 @@ local function __chunk_has_any_fragments(chunk, ...) local fs = chunk.__fragment_set + local has_f = __chunk_has_fragment + local has_fs = __chunk_has_any_fragments + + local has_p = chunk.__has_pair_fragments + if fragment_count == 1 then local f1 = ... - return fs[f1] ~= nil + return (has_p and has_f(chunk, f1)) + or (not has_p and fs[f1] ~= nil) end if fragment_count == 2 then local f1, f2 = ... - return fs[f1] ~= nil or fs[f2] ~= nil + return (has_p and (has_f(chunk, f1) or has_f(chunk, f2))) + or (not has_p and (fs[f1] ~= nil or fs[f2] ~= nil)) end if fragment_count == 3 then local f1, f2, f3 = ... - return fs[f1] ~= nil or fs[f2] ~= nil or fs[f3] ~= nil + return (has_p and (has_f(chunk, f1) or has_f(chunk, f2) or has_f(chunk, f3))) + or (not has_p and (fs[f1] ~= nil or fs[f2] ~= nil or fs[f3] ~= nil)) end if fragment_count == 4 then local f1, f2, f3, f4 = ... - return fs[f1] ~= nil or fs[f2] ~= nil or fs[f3] ~= nil or fs[f4] ~= nil + return (has_p and (has_f(chunk, f1) or has_f(chunk, f2) or has_f(chunk, f3) or has_f(chunk, f4))) + or (not has_p and (fs[f1] ~= nil or fs[f2] ~= nil or fs[f3] ~= nil or fs[f4] ~= nil)) end do local f1, f2, f3, f4 = ... - return fs[f1] ~= nil or fs[f2] ~= nil or fs[f3] ~= nil or fs[f4] ~= nil - or __chunk_has_any_fragments(chunk, __lua_select(5, ...)) + return (has_p and (has_f(chunk, f1) or has_f(chunk, f2) or has_f(chunk, f3) or has_f(chunk, f4) + or has_fs(chunk, __lua_select(5, ...)))) + or (not has_p and (fs[f1] ~= nil or fs[f2] ~= nil or fs[f3] ~= nil or fs[f4] ~= nil + or has_fs(chunk, __lua_select(5, ...)))) end end @@ -1662,10 +2248,22 @@ local function __chunk_has_any_fragment_list(chunk, fragment_list, fragment_coun local fs = chunk.__fragment_set - for i = 1, fragment_count do - local f = fragment_list[i] - if fs[f] ~= nil then - return true + local has_f = __chunk_has_fragment + local has_p = chunk.__has_pair_fragments + + if has_p then + for i = 1, fragment_count do + local f = fragment_list[i] + if has_f(chunk, f) then + return true + end + end + else + for i = 1, fragment_count do + local f = fragment_list[i] + if fs[f] ~= nil then + return true + end end end @@ -1767,6 +2365,10 @@ local function __chunk_required_fragments(chunk, req_fragment_set, req_fragment_ fragment_stack[fragment_stack_size] = nil fragment_stack_size = fragment_stack_size - 1 + if __evolved_is_pair(stack_fragment) then + stack_fragment = __evolved_unpair(stack_fragment) + end + local fragment_requires = __sorted_requires[stack_fragment] local fragment_require_list = fragment_requires and fragment_requires.__item_list --[=[@as evolved.fragment[]]=] local fragment_require_count = fragment_requires and fragment_requires.__item_count or 0 --[[@as integer]] @@ -1813,6 +2415,10 @@ local function __fragment_required_fragments(fragment, req_fragment_set, req_fra fragment_stack[fragment_stack_size] = nil fragment_stack_size = fragment_stack_size - 1 + if __evolved_is_pair(stack_fragment) then + stack_fragment = __evolved_unpair(stack_fragment) + end + local fragment_requires = __sorted_requires[stack_fragment] local fragment_require_list = fragment_requires and fragment_requires.__item_list --[=[@as evolved.fragment[]]=] local fragment_require_count = fragment_requires and fragment_requires.__item_count or 0 --[[@as integer]] @@ -2415,6 +3021,51 @@ local function __purge_chunk(chunk) end end + if chunk.__has_pair_fragments then + local wildcard = __WILDCARD_PAIR + + local major_wildcard_chunks = __major_chunks[wildcard] + local minor_wildcard_chunks = __minor_chunks[wildcard] + + if major_wildcard_chunks and __assoc_list_remove(major_wildcard_chunks, chunk) == 0 then + __major_chunks[wildcard] = nil + end + + if minor_wildcard_chunks and __assoc_list_remove(minor_wildcard_chunks, chunk) == 0 then + __minor_chunks[wildcard] = nil + end + end + + for primary_index in __lua_next, chunk.__primary_pairs do + local secondary_wildcard = __secondary_wildcard(primary_index) + + local major_wildcard_chunks = __major_chunks[secondary_wildcard] + local minor_wildcard_chunks = __minor_chunks[secondary_wildcard] + + if major_wildcard_chunks and __assoc_list_remove(major_wildcard_chunks, chunk) == 0 then + __major_chunks[secondary_wildcard] = nil + end + + if minor_wildcard_chunks and __assoc_list_remove(minor_wildcard_chunks, chunk) == 0 then + __minor_chunks[secondary_wildcard] = nil + end + end + + for secondary_index in __lua_next, chunk.__secondary_pairs do + local primary_wildcard = __primary_wildcard(secondary_index) + + local major_wildcard_chunks = __major_chunks[primary_wildcard] + local minor_wildcard_chunks = __minor_chunks[primary_wildcard] + + if major_wildcard_chunks and __assoc_list_remove(major_wildcard_chunks, chunk) == 0 then + __major_chunks[primary_wildcard] = nil + end + + if minor_wildcard_chunks and __assoc_list_remove(minor_wildcard_chunks, chunk) == 0 then + __minor_chunks[primary_wildcard] = nil + 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, @@ -2604,7 +3255,10 @@ local function __destroy_fragment_list(fragment_list, fragment_count) for i = 1, remove_fragment_policy_fragment_count do local fragment = remove_fragment_policy_fragment_list[i] - __trace_minor_chunks(fragment, __chunk_remove, fragment) + __trace_minor_chunks(fragment, __chunk_remove, + fragment, + __primary_wildcard(fragment), + __secondary_wildcard(fragment)) end __release_table(__table_pool_tag.fragment_list, remove_fragment_policy_fragment_list) @@ -4145,13 +4799,24 @@ end ---@return boolean ---@nodiscard function __evolved_alive(entity) - local entity_index = entity % 2 ^ 20 + if not __evolved_is_pair(entity) then + local entity_index = entity % 2 ^ 20 - if __freelist_ids[entity_index] ~= entity then - return false + if __freelist_ids[entity_index] ~= entity then + return false + end + + return true + else + local primary_index = entity % 2 ^ 20 + + local primary = __freelist_ids[primary_index] --[[@as evolved.id?]] + if not primary or primary % 2 ^ 20 ~= primary_index then + return false + end + + return true end - - return true end ---@param ... evolved.entity entities @@ -4200,13 +4865,24 @@ end ---@return boolean ---@nodiscard function __evolved_empty(entity) - local entity_index = entity % 2 ^ 20 + if not __evolved_is_pair(entity) then + local entity_index = entity % 2 ^ 20 - if __freelist_ids[entity_index] ~= entity then - return true + if __freelist_ids[entity_index] ~= entity then + return true + end + + return not __entity_chunks[entity_index] + else + local primary_index = entity % 2 ^ 20 + + local primary = __freelist_ids[primary_index] --[[@as evolved.id?]] + if not primary or primary % 2 ^ 20 ~= primary_index then + return true + end + + return not __entity_chunks[primary_index] end - - return not __entity_chunks[entity_index] end ---@param ... evolved.entity entities @@ -4256,19 +4932,36 @@ end ---@return boolean ---@nodiscard function __evolved_has(entity, fragment) - local entity_index = entity % 2 ^ 20 + if not __evolved_is_pair(entity) then + local entity_index = entity % 2 ^ 20 - if __freelist_ids[entity_index] ~= entity then - return false + if __freelist_ids[entity_index] ~= entity then + return false + end + + local entity_chunk = __entity_chunks[entity_index] + + if not entity_chunk then + return false + end + + return __chunk_has_fragment(entity_chunk, fragment) + else + local primary_index = entity % 2 ^ 20 + + local primary = __freelist_ids[primary_index] --[[@as evolved.id?]] + if not primary or primary % 2 ^ 20 ~= primary_index then + return false + end + + local primary_chunk = __entity_chunks[primary_index] + + if not primary_chunk then + return false + end + + return __chunk_has_fragment(primary_chunk, fragment) end - - local entity_chunk = __entity_chunks[entity_index] - - if not entity_chunk then - return false - end - - return __chunk_has_fragment(entity_chunk, fragment) end ---@param entity evolved.entity @@ -4276,19 +4969,36 @@ end ---@return boolean ---@nodiscard function __evolved_has_all(entity, ...) - local entity_index = entity % 2 ^ 20 + if not __evolved_is_pair(entity) then + local entity_index = entity % 2 ^ 20 - if __freelist_ids[entity_index] ~= entity then - return __lua_select('#', ...) == 0 + if __freelist_ids[entity_index] ~= entity then + return __lua_select('#', ...) == 0 + end + + local entity_chunk = __entity_chunks[entity_index] + + if not entity_chunk then + return __lua_select('#', ...) == 0 + end + + return __chunk_has_all_fragments(entity_chunk, ...) + else + local primary_index = entity % 2 ^ 20 + + local primary = __freelist_ids[primary_index] --[[@as evolved.id?]] + if not primary or primary % 2 ^ 20 ~= primary_index then + return __lua_select('#', ...) == 0 + end + + local primary_chunk = __entity_chunks[primary_index] + + if not primary_chunk then + return __lua_select('#', ...) == 0 + end + + return __chunk_has_all_fragments(primary_chunk, ...) end - - local entity_chunk = __entity_chunks[entity_index] - - if not entity_chunk then - return __lua_select('#', ...) == 0 - end - - return __chunk_has_all_fragments(entity_chunk, ...) end ---@param entity evolved.entity @@ -4296,19 +5006,36 @@ end ---@return boolean ---@nodiscard function __evolved_has_any(entity, ...) - local entity_index = entity % 2 ^ 20 + if not __evolved_is_pair(entity) then + local entity_index = entity % 2 ^ 20 - if __freelist_ids[entity_index] ~= entity then - return false + if __freelist_ids[entity_index] ~= entity then + return false + end + + local entity_chunk = __entity_chunks[entity_index] + + if not entity_chunk then + return false + end + + return __chunk_has_any_fragments(entity_chunk, ...) + else + local primary_index = entity % 2 ^ 20 + + local primary = __freelist_ids[primary_index] --[[@as evolved.id?]] + if not primary or primary % 2 ^ 20 ~= primary_index then + return false + end + + local primary_chunk = __entity_chunks[primary_index] + + if not primary_chunk then + return false + end + + return __chunk_has_any_fragments(primary_chunk, ...) end - - local entity_chunk = __entity_chunks[entity_index] - - if not entity_chunk then - return false - end - - return __chunk_has_any_fragments(entity_chunk, ...) end ---@param entity evolved.entity @@ -4316,20 +5043,38 @@ end ---@return evolved.component ... components ---@nodiscard function __evolved_get(entity, ...) - local entity_index = entity % 2 ^ 20 + if not __evolved_is_pair(entity) then + local entity_index = entity % 2 ^ 20 - if __freelist_ids[entity_index] ~= entity then - return + if __freelist_ids[entity_index] ~= entity then + return + end + + local entity_chunk = __entity_chunks[entity_index] + + if not entity_chunk then + return + end + + local entity_place = __entity_places[entity_index] + return __chunk_get_components(entity_chunk, entity_place, ...) + else + local primary_index = entity % 2 ^ 20 + + local primary = __freelist_ids[primary_index] --[[@as evolved.id?]] + if not primary or primary % 2 ^ 20 ~= primary_index then + return + end + + local primary_chunk = __entity_chunks[primary_index] + + if not primary_chunk then + return + end + + local primary_place = __entity_places[primary_index] + return __chunk_get_components(primary_chunk, primary_place, ...) end - - local entity_chunk = __entity_chunks[entity_index] - - if not entity_chunk then - return - end - - local entity_place = __entity_places[entity_index] - return __chunk_get_components(entity_chunk, entity_place, ...) end ---@param entity evolved.entity @@ -4337,6 +5082,14 @@ end ---@param component evolved.component function __evolved_set(entity, fragment, component) if __debug_mode then + if __evolved_is_pair(entity) then + __error_fmt('the pair (%s) cannot be used as an entity', __id_name(entity)) + end + + if __evolved_is_wildcard(fragment) then + __error_fmt('the wildcard fragment (%s) cannot be used as a fragment', __id_name(fragment)) + end + if not __evolved_alive(entity) then __error_fmt('the entity (%s) is not alive and cannot be used', __id_name(entity)) end @@ -4582,6 +5335,11 @@ function __evolved_remove(entity, ...) return end + if __evolved_is_pair(entity) then + -- pairs cannot have fragments, nothing to remove + return + end + local entity_index = entity % 2 ^ 20 if __freelist_ids[entity_index] ~= entity then @@ -4700,7 +5458,9 @@ function __evolved_clear(...) local entity = __lua_select(argument_index, ...) local entity_index = entity % 2 ^ 20 - if __freelist_ids[entity_index] ~= entity then + if __evolved_is_pair(entity) then + -- pairs cannot have fragments, nothing to clear + elseif __freelist_ids[entity_index] ~= entity then -- this entity is not alive, nothing to clear else local chunk = entity_chunks[entity_index] @@ -4776,10 +5536,15 @@ function __evolved_destroy(...) local entity = __lua_select(argument_index, ...) local entity_index = entity % 2 ^ 20 - if __freelist_ids[entity_index] ~= entity then + if __evolved_is_pair(entity) then + -- pairs cannot be destroyed, nothing to do + elseif __freelist_ids[entity_index] ~= entity then -- this entity is not alive, nothing to destroy else - local is_fragment = minor_chunks[entity] + local is_fragment = + minor_chunks[entity] or + minor_chunks[__primary_wildcard(entity)] or + minor_chunks[__secondary_wildcard(entity)] if not is_fragment then purging_entity_count = purging_entity_count + 1 @@ -4814,6 +5579,14 @@ end ---@param component evolved.component function __evolved_batch_set(query, fragment, component) if __debug_mode then + if __evolved_is_pair(query) then + __error_fmt('the pair (%s) cannot be used as a query', __id_name(query)) + end + + if __evolved_is_wildcard(fragment) then + __error_fmt('the wildcard fragment (%s) cannot be used as a fragment', __id_name(fragment)) + end + if not __evolved_alive(query) then __error_fmt('the query (%s) is not alive and cannot be used', __id_name(query)) end @@ -4967,7 +5740,10 @@ function __evolved_batch_destroy(...) for i = 1, entity_count do local entity = entity_list[i] - local is_fragment = minor_chunks[entity] + local is_fragment = + minor_chunks[entity] or + minor_chunks[__primary_wildcard(entity)] or + minor_chunks[__secondary_wildcard(entity)] if not is_fragment then purging_entity_count = purging_entity_count + 1 @@ -5010,6 +5786,11 @@ end ---@return evolved.each_state? iterator_state ---@nodiscard function __evolved_each(entity) + if __evolved_is_pair(entity) then + -- pairs cannot be used as entities, nothing to iterate + return __iterator_fns.__each_iterator + end + local entity_index = entity % 2 ^ 20 if __freelist_ids[entity_index] ~= entity then @@ -5043,6 +5824,11 @@ end ---@return evolved.execute_state? iterator_state ---@nodiscard function __evolved_execute(query) + if __evolved_is_pair(query) then + -- pairs cannot be used as queries, nothing to execute + return __iterator_fns.__execute_major_iterator + end + local query_index = query % 2 ^ 20 if __freelist_ids[query_index] ~= query then @@ -5067,57 +5853,134 @@ function __evolved_execute(query) if query_include_count > 0 then local query_major = query_include_list[query_include_count] - local major_chunks = __major_chunks[query_major] - local major_chunk_list = major_chunks and major_chunks.__item_list --[=[@as evolved.chunk[]]=] - local major_chunk_count = major_chunks and major_chunks.__item_count or 0 --[[@as integer]] + if __evolved_is_wildcard(query_major) then + local minor_chunks = __minor_chunks[query_major] + local minor_chunk_list = minor_chunks and minor_chunks.__item_list --[=[@as evolved.chunk[]]=] + local minor_chunk_count = minor_chunks and minor_chunks.__item_count or 0 --[[@as integer]] - for major_chunk_index = 1, major_chunk_count do - local major_chunk = major_chunk_list[major_chunk_index] + for query_include_index = 1, query_include_count - 1 do + local query_minor = query_include_list[query_include_index] - local is_major_chunk_matched = true + local query_chunks = __minor_chunks[query_minor] + local query_chunk_list = query_chunks and query_chunks.__item_list --[=[@as evolved.chunk[]]=] + local query_chunk_count = query_chunks and query_chunks.__item_count or 0 --[[@as integer]] - if is_major_chunk_matched and query_include_count > 1 then - is_major_chunk_matched = __chunk_has_all_fragment_list( - major_chunk, query_include_list, query_include_count - 1) - end + if query_chunk_count < minor_chunk_count then + minor_chunks, minor_chunk_list, minor_chunk_count = + query_chunks, query_chunk_list, query_chunk_count - if is_major_chunk_matched and query_exclude_count > 0 then - is_major_chunk_matched = not __chunk_has_any_fragment_list( - major_chunk, query_exclude_list, query_exclude_count) - end - - if is_major_chunk_matched and major_chunk.__has_explicit_minors then - local major_chunk_minor_list = major_chunk.__fragment_list - local major_chunk_minor_count = major_chunk.__fragment_count - 1 - - for major_chunk_fragment_index = 1, major_chunk_minor_count do - local major_chunk_minor = major_chunk_minor_list[major_chunk_fragment_index] - - local is_major_chunk_minor_included = - query_include_set[major_chunk_minor] - - if not is_major_chunk_minor_included and __evolved_has(major_chunk_minor, __EXPLICIT) then - is_major_chunk_matched = false + if query_chunk_count == 0 then break end end end - if is_major_chunk_matched then - chunk_stack_size = chunk_stack_size + 1 - chunk_stack[chunk_stack_size] = major_chunk + for minor_chunk_index = 1, minor_chunk_count do + local minor_chunk = minor_chunk_list[minor_chunk_index] + + local is_minor_chunk_matched = true + + if is_minor_chunk_matched and minor_chunk.__entity_count == 0 then + is_minor_chunk_matched = false + end + + if is_minor_chunk_matched then + is_minor_chunk_matched = __chunk_has_all_fragment_list( + minor_chunk, query_include_list, query_include_count) + end + + if is_minor_chunk_matched and query_exclude_count > 0 then + is_minor_chunk_matched = not __chunk_has_any_fragment_list( + minor_chunk, query_exclude_list, query_exclude_count) + end + + if is_minor_chunk_matched and minor_chunk.__has_explicit_fragments then + local minor_chunk_fragment_list = minor_chunk.__fragment_list + local minor_chunk_fragment_count = minor_chunk.__fragment_count + + for minor_chunk_fragment_index = 1, minor_chunk_fragment_count do + local minor_chunk_fragment = minor_chunk_fragment_list[minor_chunk_fragment_index] + + local is_minor_chunk_fragment_included = + query_include_set[minor_chunk_fragment] or + query_include_set[__secondary_wildcard(__evolved_unpack(minor_chunk_fragment))] + + if not is_minor_chunk_fragment_included and __evolved_has(minor_chunk_fragment, __EXPLICIT) then + is_minor_chunk_matched = false + break + end + end + end + + if is_minor_chunk_matched then + chunk_stack_size = chunk_stack_size + 1 + chunk_stack[chunk_stack_size] = minor_chunk + end end + + ---@type evolved.execute_state + local execute_state = __acquire_table(__table_pool_tag.execute_state) + + execute_state[1] = __structural_changes + execute_state[2] = chunk_stack + execute_state[3] = chunk_stack_size + execute_state[4] = query_exclude_set + + return __iterator_fns.__execute_minor_iterator, execute_state + else + local major_chunks = __major_chunks[query_major] + local major_chunk_list = major_chunks and major_chunks.__item_list --[=[@as evolved.chunk[]]=] + local major_chunk_count = major_chunks and major_chunks.__item_count or 0 --[[@as integer]] + + for major_chunk_index = 1, major_chunk_count do + local major_chunk = major_chunk_list[major_chunk_index] + + local is_major_chunk_matched = true + + if is_major_chunk_matched and query_include_count > 1 then + is_major_chunk_matched = __chunk_has_all_fragment_list( + major_chunk, query_include_list, query_include_count - 1) + end + + if is_major_chunk_matched and query_exclude_count > 0 then + is_major_chunk_matched = not __chunk_has_any_fragment_list( + major_chunk, query_exclude_list, query_exclude_count) + end + + if is_major_chunk_matched and major_chunk.__has_explicit_minors then + local major_chunk_minor_list = major_chunk.__fragment_list + local major_chunk_minor_count = major_chunk.__fragment_count - 1 + + for major_chunk_fragment_index = 1, major_chunk_minor_count do + local major_chunk_minor = major_chunk_minor_list[major_chunk_fragment_index] + + local is_major_chunk_minor_included = + query_include_set[major_chunk_minor] or + query_include_set[__secondary_wildcard(__evolved_unpack(major_chunk_minor))] + + if not is_major_chunk_minor_included and __evolved_has(major_chunk_minor, __EXPLICIT) then + is_major_chunk_matched = false + break + end + end + end + + if is_major_chunk_matched then + chunk_stack_size = chunk_stack_size + 1 + chunk_stack[chunk_stack_size] = major_chunk + end + end + + ---@type evolved.execute_state + local execute_state = __acquire_table(__table_pool_tag.execute_state) + + execute_state[1] = __structural_changes + execute_state[2] = chunk_stack + execute_state[3] = chunk_stack_size + execute_state[4] = query_exclude_set + + return __iterator_fns.__execute_major_iterator, execute_state end - - ---@type evolved.execute_state - local execute_state = __acquire_table(__table_pool_tag.execute_state) - - execute_state[1] = __structural_changes - execute_state[2] = chunk_stack - execute_state[3] = chunk_stack_size - execute_state[4] = query_exclude_set - - return __iterator_fns.__execute_major_iterator, execute_state else for _, root_chunk in __lua_next, __root_chunks do local is_root_chunk_matched = true @@ -5161,7 +6024,9 @@ function __evolved_process(...) ---@type evolved.system local system = __lua_select(argument_index, ...) - if not __evolved_alive(system) then + if __evolved_is_pair(system) then + -- pairs cannot be used as systems, nothing to process + elseif not __evolved_alive(system) then -- this system is not alive, nothing to process elseif __evolved_has(system, __DISABLED) then -- this system is disabled, nothing to process @@ -5242,6 +6107,277 @@ function __evolved_collect_garbage() __evolved_commit() end +---@param primary evolved.id +---@param secondary evolved.id +---@return evolved.id pair +---@nodiscard +function __evolved_pair(primary, secondary) + local options = __PAIR_OPTS + + if primary == __ANY and secondary == __ANY then + options = __WILDCARD_OPTS + elseif primary == __ANY then + options = __PRIMARY_WILDCARD_OPTS + elseif secondary == __ANY then + options = __SECONDARY_WILDCARD_OPTS + end + + return primary % 2 ^ 20 + + secondary % 2 ^ 20 * 2 ^ 20 + + options * 2 ^ 40 --[[@as evolved.id]] +end + +---@param pair evolved.id +---@return evolved.id primary +---@return evolved.id secondary +---@nodiscard +function __evolved_unpair(pair) + local primary_index = pair % 2 ^ 20 + local secondary_index = (pair - primary_index) / 2 ^ 20 % 2 ^ 20 + + local primary = __freelist_ids[primary_index] --[[@as evolved.id]] + if not primary or primary % 2 ^ 20 ~= primary_index then + __error_fmt('the primary fragment of the pair (%s) is not alive and cannot be unpaired', + __id_name(pair)) + end + + local secondary = __freelist_ids[secondary_index] --[[@as evolved.id]] + if not secondary or secondary % 2 ^ 20 ~= secondary_index then + __error_fmt('the secondary fragment of the pair (%s) is not alive and cannot be unpaired', + __id_name(pair)) + end + + return primary, secondary +end + +---@param id evolved.id +---@return boolean +---@nodiscard +function __evolved_is_pair(id) + return id % 2 ^ 41 >= 2 ^ 40 +end + +---@param id evolved.id +---@return boolean +---@nodiscard +function __evolved_is_wildcard(id) + return id % 2 ^ 43 >= 2 ^ 41 +end + +---@param entity evolved.entity +---@param secondary evolved.fragment +---@param index? integer +---@return evolved.fragment? primary +---@return evolved.component? component +---@nodiscard +function __evolved_primary(entity, secondary, index) + index = index or 1 + + if __evolved_is_pair(entity) then + -- pairs are always empty + return + end + + local entity_index = entity % 2 ^ 20 + + if __freelist_ids[entity_index] ~= entity then + -- non-alive entities do not have any fragments + return + end + + local chunk = __entity_chunks[entity_index] + local place = __entity_places[entity_index] + + local secondary_fragments = chunk and chunk.__secondary_pairs[secondary % 2 ^ 20] + local secondary_fragment_list = secondary_fragments and secondary_fragments.__item_list + local secondary_fragment_count = secondary_fragments and secondary_fragments.__item_count or 0 + + if index < 1 or index > secondary_fragment_count then + return + end + + local secondary_fragment = secondary_fragment_list[index] + local primary, _ = __evolved_unpair(secondary_fragment) + + local component_index = chunk.__component_indices[secondary_fragment] + local component_storage = chunk.__component_storages[component_index] + + return primary, component_storage and component_storage[place] +end + +---@param entity evolved.entity +---@param primary evolved.fragment +---@param index? integer +---@return evolved.fragment? secondary +---@return evolved.component? component +---@nodiscard +function __evolved_secondary(entity, primary, index) + index = index or 1 + + if __evolved_is_pair(entity) then + -- pairs are always empty + return + end + + local entity_index = entity % 2 ^ 20 + + if __freelist_ids[entity_index] ~= entity then + -- non-alive entities do not have any fragments + return + end + + local chunk = __entity_chunks[entity_index] + local place = __entity_places[entity_index] + + local primary_fragments = chunk and chunk.__primary_pairs[primary % 2 ^ 20] + local primary_fragment_list = primary_fragments and primary_fragments.__item_list + local primary_fragment_count = primary_fragments and primary_fragments.__item_count or 0 + + if index < 1 or index > primary_fragment_count then + return + end + + local primary_fragment = primary_fragment_list[index] + local _, secondary = __evolved_unpair(primary_fragment) + + local component_index = chunk.__component_indices[primary_fragment] + local component_storage = chunk.__component_storages[component_index] + + return secondary, component_storage and component_storage[place] +end + +---@param entity evolved.entity +---@param secondary evolved.fragment +---@return evolved.primaries_iterator iterator +---@return evolved.primaries_state? iterator_state +---@nodiscard +function __evolved_primaries(entity, secondary) + if __evolved_is_pair(entity) then + -- pairs are always empty + return __iterator_fns.__primaries_iterator + end + + local entity_index = entity % 2 ^ 20 + + if __freelist_ids[entity_index] ~= entity then + -- non-alive entities do not have any fragments + return __iterator_fns.__primaries_iterator + end + + local chunk = __entity_chunks[entity_index] + local place = __entity_places[entity_index] + + local secondary_index = secondary % 2 ^ 20 + local secondary_fragments = chunk and chunk.__secondary_pairs[secondary_index] + + if not secondary_fragments or secondary_fragments.__item_count == 0 then + -- no primaries for this secondary + return __iterator_fns.__primaries_iterator + end + + ---@type evolved.primaries_state + local primaries_state = __acquire_table(__table_pool_tag.primaries_state) + + primaries_state[1] = __structural_changes + primaries_state[2] = chunk + primaries_state[3] = place + primaries_state[4] = secondary_index + primaries_state[5] = 1 + + return __iterator_fns.__primaries_iterator, primaries_state +end + +---@param entity evolved.entity +---@param primary evolved.fragment +---@return evolved.secondaries_iterator iterator +---@return evolved.secondaries_state? iterator_state +---@nodiscard +function __evolved_secondaries(entity, primary) + if __evolved_is_pair(entity) then + -- pairs are always empty + return __iterator_fns.__secondaries_iterator + end + + local entity_index = entity % 2 ^ 20 + + if __freelist_ids[entity_index] ~= entity then + -- non-alive entities do not have any fragments + return __iterator_fns.__secondaries_iterator + end + + local chunk = __entity_chunks[entity_index] + local place = __entity_places[entity_index] + + local primary_index = primary % 2 ^ 20 + local primary_fragments = chunk and chunk.__primary_pairs[primary_index] + + if not primary_fragments or primary_fragments.__item_count == 0 then + -- no secondaries for this primary + return __iterator_fns.__secondaries_iterator + end + + ---@type evolved.secondaries_state + local secondaries_state = __acquire_table(__table_pool_tag.secondaries_state) + + secondaries_state[1] = __structural_changes + secondaries_state[2] = chunk + secondaries_state[3] = place + secondaries_state[4] = primary_index + secondaries_state[5] = 1 + + return __iterator_fns.__secondaries_iterator, secondaries_state +end + +---@param entity evolved.entity +---@param secondary evolved.fragment +---@return integer +---@nodiscard +function __evolved_primary_count(entity, secondary) + if __evolved_is_pair(entity) then + -- pairs are always empty + return 0 + end + + local entity_index = entity % 2 ^ 20 + + if __freelist_ids[entity_index] ~= entity then + -- non-alive entities do not have any fragments + return 0 + end + + local chunk = __entity_chunks[entity_index] + + local secondary_index = secondary % 2 ^ 20 + local secondary_fragments = chunk and chunk.__secondary_pairs[secondary_index] + + return secondary_fragments and secondary_fragments.__item_count or 0 +end + +---@param entity evolved.entity +---@param primary evolved.fragment +---@return integer +---@nodiscard +function __evolved_secondary_count(entity, primary) + if __evolved_is_pair(entity) then + -- pairs are always empty + return 0 + end + + local entity_index = entity % 2 ^ 20 + + if __freelist_ids[entity_index] ~= entity then + -- non-alive entities do not have any fragments + return 0 + end + + local chunk = __entity_chunks[entity_index] + + local primary_index = primary % 2 ^ 20 + local primary_fragments = chunk and chunk.__primary_pairs[primary_index] + + return primary_fragments and primary_fragments.__item_count or 0 +end + --- --- --- @@ -5430,6 +6566,30 @@ function __builder_mt:has(fragment) return true end + local primary_pairs = self.__primary_pairs + local secondary_pairs = self.__secondary_pairs + + local maybe_has_pairs = primary_pairs and secondary_pairs + + if maybe_has_pairs and __evolved_is_wildcard(fragment) then + ---@cast primary_pairs -? + ---@cast secondary_pairs -? + + local fragment_primary_index, fragment_secondary_index, fragment_opts = + __evolved_unpack(fragment) + + if fragment_opts == __WILDCARD_OPTS then + return __lua_next(primary_pairs) ~= nil + and __lua_next(secondary_pairs) ~= nil + elseif fragment_opts == __PRIMARY_WILDCARD_OPTS then + local secondary_fragments = secondary_pairs[fragment_secondary_index] + return secondary_fragments ~= nil and secondary_fragments.__item_count > 0 + elseif fragment_opts == __SECONDARY_WILDCARD_OPTS then + local primary_fragments = primary_pairs[fragment_primary_index] + return primary_fragments ~= nil and primary_fragments.__item_count > 0 + end + end + return false end @@ -5445,30 +6605,41 @@ function __builder_mt:has_all(...) local cs = self.__components + local has_f = self.has + local has_fs = self.has_all + + local m_has_p = self.__primary_pairs and self.__secondary_pairs + if fragment_count == 1 then local f1 = ... - return cs[f1] ~= nil + return (m_has_p and has_f(self, f1)) + or (not m_has_p and cs[f1] ~= nil) end if fragment_count == 2 then local f1, f2 = ... - return cs[f1] ~= nil and cs[f2] ~= nil + return (m_has_p and has_f(self, f1) and has_f(self, f2)) + or (not m_has_p and cs[f1] ~= nil and cs[f2] ~= nil) end if fragment_count == 3 then local f1, f2, f3 = ... - return cs[f1] ~= nil and cs[f2] ~= nil and cs[f3] ~= nil + return (m_has_p and has_f(self, f1) and has_f(self, f2) and has_f(self, f3)) + or (not m_has_p and cs[f1] ~= nil and cs[f2] ~= nil and cs[f3] ~= nil) end if fragment_count == 4 then local f1, f2, f3, f4 = ... - return cs[f1] ~= nil and cs[f2] ~= nil and cs[f3] ~= nil and cs[f4] ~= nil + return (m_has_p and has_f(self, f1) and has_f(self, f2) and has_f(self, f3) and has_f(self, f4)) + or (not m_has_p and cs[f1] ~= nil and cs[f2] ~= nil and cs[f3] ~= nil and cs[f4] ~= nil) end do local f1, f2, f3, f4 = ... - return cs[f1] ~= nil and cs[f2] ~= nil and cs[f3] ~= nil and cs[f4] ~= nil - and self:has_all(__lua_select(5, ...)) + return (m_has_p and has_f(self, f1) and has_f(self, f2) and has_f(self, f3) and has_f(self, f4) + and has_fs(self, __lua_select(5, ...))) + or (not m_has_p and cs[f1] ~= nil and cs[f2] ~= nil and cs[f3] ~= nil and cs[f4] ~= nil + and has_fs(self, __lua_select(5, ...))) end end @@ -5484,30 +6655,41 @@ function __builder_mt:has_any(...) local cs = self.__components + local has_f = self.has + local has_fs = self.has_any + + local m_has_p = self.__primary_pairs and self.__secondary_pairs + if fragment_count == 1 then local f1 = ... - return cs[f1] ~= nil + return (m_has_p and has_f(self, f1)) + or (not m_has_p and cs[f1] ~= nil) end if fragment_count == 2 then local f1, f2 = ... - return cs[f1] ~= nil or cs[f2] ~= nil + return (m_has_p and (has_f(self, f1) or has_f(self, f2))) + or (not m_has_p and (cs[f1] ~= nil or cs[f2] ~= nil)) end if fragment_count == 3 then local f1, f2, f3 = ... - return cs[f1] ~= nil or cs[f2] ~= nil or cs[f3] ~= nil + return (m_has_p and (has_f(self, f1) or has_f(self, f2) or has_f(self, f3))) + or (not m_has_p and (cs[f1] ~= nil or cs[f2] ~= nil or cs[f3] ~= nil)) end if fragment_count == 4 then local f1, f2, f3, f4 = ... - return cs[f1] ~= nil or cs[f2] ~= nil or cs[f3] ~= nil or cs[f4] ~= nil + return (m_has_p and (has_f(self, f1) or has_f(self, f2) or has_f(self, f3) or has_f(self, f4))) + or (not m_has_p and (cs[f1] ~= nil or cs[f2] ~= nil or cs[f3] ~= nil or cs[f4] ~= nil)) end do local f1, f2, f3, f4 = ... - return cs[f1] ~= nil or cs[f2] ~= nil or cs[f3] ~= nil or cs[f4] ~= nil - or self:has_any(__lua_select(5, ...)) + return (m_has_p and (has_f(self, f1) or has_f(self, f2) or has_f(self, f3) or has_f(self, f4) + or has_fs(self, __lua_select(5, ...)))) + or (not m_has_p and (cs[f1] ~= nil or cs[f2] ~= nil or cs[f3] ~= nil or cs[f4] ~= nil + or has_fs(self, __lua_select(5, ...)))) end end @@ -5558,6 +6740,10 @@ function __builder_mt:set(fragment, component) if not __evolved_alive(fragment) then __error_fmt('the fragment (%s) is not alive and cannot be used', __id_name(fragment)) end + + if __evolved_is_wildcard(fragment) then + __error_fmt('the wildcard fragment (%s) cannot be used as a fragment', __id_name(fragment)) + end end do @@ -5573,6 +6759,40 @@ function __builder_mt:set(fragment, component) self.__components[fragment] = new_component end + if __evolved_is_pair(fragment) then + local fragment_primary_index, fragment_secondary_index = + __evolved_unpack(fragment) + + local primary_pairs = self.__primary_pairs + local secondary_pairs = self.__secondary_pairs + + if not primary_pairs then + primary_pairs = {} + self.__primary_pairs = primary_pairs + end + + if not secondary_pairs then + secondary_pairs = {} + self.__secondary_pairs = secondary_pairs + end + + local primary_fragments = primary_pairs[fragment_primary_index] + local secondary_fragments = secondary_pairs[fragment_secondary_index] + + if not primary_fragments then + primary_fragments = __assoc_list_new(4) + primary_pairs[fragment_primary_index] = primary_fragments + end + + if not secondary_fragments then + secondary_fragments = __assoc_list_new(4) + secondary_pairs[fragment_secondary_index] = secondary_fragments + end + + __assoc_list_insert(primary_fragments, fragment) + __assoc_list_insert(secondary_fragments, fragment) + end + return self end @@ -5585,42 +6805,100 @@ function __builder_mt:remove(...) return self end - local cs = self.__components + local fragment = ... - if fragment_count == 1 then - local f1 = ... - cs[f1] = nil - return self + local components = self.__components + + components[fragment] = nil + + local primary_pairs = self.__primary_pairs + local secondary_pairs = self.__secondary_pairs + + local maybe_has_pairs = primary_pairs and secondary_pairs + + if maybe_has_pairs and __evolved_is_wildcard(fragment) then + ---@cast primary_pairs -? + ---@cast secondary_pairs -? + + local fragment_primary_index, fragment_secondary_index, fragment_opts = + __evolved_unpack(fragment) + + if fragment_opts == __WILDCARD_OPTS then + for primary_index, primary_fragments in __lua_next, primary_pairs do + local primary_fragment_list = primary_fragments.__item_list + local primary_fragment_count = primary_fragments.__item_count + + for primary_fragment_index = 1, primary_fragment_count do + local primary_fragment = primary_fragment_list[primary_fragment_index] + components[primary_fragment] = nil + end + + primary_pairs[primary_index] = nil + end + + for secondary_index, secondary_fragments in __lua_next, secondary_pairs do + local secondary_fragment_list = secondary_fragments.__item_list + local secondary_fragment_count = secondary_fragments.__item_count + + for secondary_fragment_index = 1, secondary_fragment_count do + local secondary_fragment = secondary_fragment_list[secondary_fragment_index] + components[secondary_fragment] = nil + end + + secondary_pairs[secondary_index] = nil + end + elseif fragment_opts == __PRIMARY_WILDCARD_OPTS then + local secondary_fragments = secondary_pairs[fragment_secondary_index] + local secondary_fragment_list = secondary_fragments and secondary_fragments.__item_list + local secondary_fragment_count = secondary_fragments and secondary_fragments.__item_count or 0 + + for secondary_fragment_index = 1, secondary_fragment_count do + local secondary_fragment = secondary_fragment_list[secondary_fragment_index] + components[secondary_fragment] = nil + + local secondary_fragment_primary_index, _ = __evolved_unpack(secondary_fragment) + if __assoc_list_remove(primary_pairs[secondary_fragment_primary_index], secondary_fragment) == 0 then + primary_pairs[secondary_fragment_primary_index] = nil + end + end + + secondary_pairs[fragment_secondary_index] = nil + elseif fragment_opts == __SECONDARY_WILDCARD_OPTS then + local primary_fragments = primary_pairs[fragment_primary_index] + local primary_fragment_list = primary_fragments and primary_fragments.__item_list + local primary_fragment_count = primary_fragments and primary_fragments.__item_count or 0 + + for primary_fragment_index = 1, primary_fragment_count do + local primary_fragment = primary_fragment_list[primary_fragment_index] + components[primary_fragment] = nil + + local _, primary_fragment_secondary_index = __evolved_unpack(primary_fragment) + if __assoc_list_remove(secondary_pairs[primary_fragment_secondary_index], primary_fragment) == 0 then + secondary_pairs[primary_fragment_secondary_index] = nil + end + end + + primary_pairs[fragment_primary_index] = nil + end end - if fragment_count == 2 then - local f1, f2 = ... - cs[f1] = nil; cs[f2] = nil - return self - end - - if fragment_count == 3 then - local f1, f2, f3 = ... - cs[f1] = nil; cs[f2] = nil; cs[f3] = nil - return self - end - - if fragment_count == 4 then - local f1, f2, f3, f4 = ... - cs[f1] = nil; cs[f2] = nil; cs[f3] = nil; cs[f4] = nil - return self - end - - do - local f1, f2, f3, f4 = ... - cs[f1] = nil; cs[f2] = nil; cs[f3] = nil; cs[f4] = nil - return self:remove(__lua_select(5, ...)) - end + return fragment_count > 1 and self:remove(__lua_select(2, ...)) or self end ---@return evolved.builder builder function __builder_mt:clear() - __lua_table_clear(self.__components) + if self.__components then + __lua_table_clear(self.__components) + end + + if self.__primary_pairs then + __lua_table_clear(self.__primary_pairs) + end + + if self.__secondary_pairs then + __lua_table_clear(self.__secondary_pairs) + end + return self end @@ -5856,6 +7134,8 @@ __evolved_set(__REQUIRES, __ON_REMOVE, __update_major_chunks_hook) --- --- +__evolved_set(__ANY, __NAME, 'ANY') + __evolved_set(__TAG, __NAME, 'TAG') __evolved_set(__NAME, __NAME, 'NAME') @@ -5896,6 +7176,8 @@ __evolved_set(__DESTRUCTION_POLICY_REMOVE_FRAGMENT, __NAME, 'DESTRUCTION_POLICY_ --- --- +__evolved_set(__ANY, __INTERNAL) + __evolved_set(__TAG, __INTERNAL) __evolved_set(__NAME, __INTERNAL) @@ -5936,6 +7218,8 @@ __evolved_set(__DESTRUCTION_POLICY_REMOVE_FRAGMENT, __INTERNAL) --- --- +__evolved_set(__ANY, __TAG) + __evolved_set(__TAG, __TAG) __evolved_set(__UNIQUE, __TAG) @@ -6100,6 +7384,8 @@ end) --- --- +evolved.ANY = __ANY + evolved.TAG = __TAG evolved.NAME = __NAME @@ -6136,7 +7422,7 @@ evolved.DESTRUCTION_POLICY_REMOVE_FRAGMENT = __DESTRUCTION_POLICY_REMOVE_FRAGMEN --- --- ---- Functions +--- Core Functions --- --- @@ -6187,6 +7473,27 @@ evolved.collect_garbage = __evolved_collect_garbage evolved.chunk = __evolved_chunk evolved.builder = __evolved_builder +--- +--- +--- Relation Functions +--- +--- + +evolved.pair = __evolved_pair +evolved.unpair = __evolved_unpair + +evolved.is_pair = __evolved_is_pair +evolved.is_wildcard = __evolved_is_wildcard + +evolved.primary = __evolved_primary +evolved.secondary = __evolved_secondary + +evolved.primaries = __evolved_primaries +evolved.secondaries = __evolved_secondaries + +evolved.primary_count = __evolved_primary_count +evolved.secondary_count = __evolved_secondary_count + --- --- --- From f2a8ee5b831413e84a95d4f1555d5b6df54661c9 Mon Sep 17 00:00:00 2001 From: BlackMATov Date: Thu, 21 Aug 2025 17:58:03 +0700 Subject: [PATCH 02/12] little style fixes --- develop/testing/pairs_tests.lua | 12 +++++ evolved.lua | 77 +++++++++++++++------------------ 2 files changed, 47 insertions(+), 42 deletions(-) diff --git a/develop/testing/pairs_tests.lua b/develop/testing/pairs_tests.lua index e5982bd..58ed05d 100644 --- a/develop/testing/pairs_tests.lua +++ b/develop/testing/pairs_tests.lua @@ -1600,8 +1600,20 @@ do end end +do + local p, s = evo.id(2) + evo.set(p, evo.NAME, 'p') + evo.set(s, evo.NAME, 's') + assert(evo.name(evo.pair(p, s)) == '${p,s}') + assert(evo.name(evo.pair(evo.ANY, s)) == '${ANY,s}') + assert(evo.name(evo.pair(p, evo.ANY)) == '${p,ANY}') + assert(evo.name(evo.pair(evo.ANY, evo.ANY)) == '${ANY,ANY}') +end + -- TODO -- builder:has/has_all/has_any should work with wildcards / remove too? -- should we provide wildcard support for get operations? -- prevent setting pairs with dead secondary fragments -- process evo.ANY as single wildcard +-- should we provide an evolved.pair type? +-- how should the destroy function handle pairs? diff --git a/evolved.lua b/evolved.lua index a1fb6fe..4ad5de0 100644 --- a/evolved.lua +++ b/evolved.lua @@ -131,6 +131,11 @@ local evolved = { | ANY WILDCARD | RSVD | 11 | 1 | ANY index | ANY index | \------------------------------------------------------------------]=] +local __PAIR_OPTS = 1 -- 0b001 +local __PRI_WILDCARD_OPTS = 3 -- 0b011 +local __SEC_WILDCARD_OPTS = 5 -- 0b101 +local __ANY_WILDCARD_OPTS = 7 -- 0b111 + --- --- --- @@ -817,16 +822,11 @@ local __DESTRUCTION_POLICY_REMOVE_FRAGMENT = __acquire_id() --- --- -local __PAIR_OPTS = 1 -- 0b001 -local __WILDCARD_OPTS = 7 -- 0b111 -local __PRIMARY_WILDCARD_OPTS = 3 -- 0b011 -local __SECONDARY_WILDCARD_OPTS = 5 -- 0b101 - local __ANY_INDEX = __ANY % 2 ^ 20 --[[@as integer]] -local __WILDCARD_PAIR = __ANY_INDEX +local __ANY_WILDCARD = __ANY_INDEX + __ANY_INDEX * 2 ^ 20 - + __WILDCARD_OPTS * 2 ^ 40 --[[@as evolved.id]] + + __ANY_WILDCARD_OPTS * 2 ^ 40 --[[@as evolved.id]] --- --- @@ -835,35 +835,30 @@ local __WILDCARD_PAIR = __ANY_INDEX --- local __safe_tbls = { - ---@type table __EMPTY_FRAGMENT_SET = __lua_setmetatable({}, { __tostring = function() return 'empty fragment set' end, __newindex = function() __error_fmt 'attempt to modify empty fragment set' end - }), + }) --[[@as table]], - ---@type evolved.fragment[] __EMPTY_FRAGMENT_LIST = __lua_setmetatable({}, { __tostring = function() return 'empty fragment list' end, __newindex = function() __error_fmt 'attempt to modify empty fragment list' end - }), + }) --[=[@as evolved.fragment[]]=], - ---@type table __EMPTY_COMPONENT_MAP = __lua_setmetatable({}, { __tostring = function() return 'empty component map' end, __newindex = function() __error_fmt 'attempt to modify empty component map' end - }), + }) --[[@as table]], - ---@type evolved.component[] __EMPTY_COMPONENT_LIST = __lua_setmetatable({}, { __tostring = function() return 'empty component list' end, __newindex = function() __error_fmt 'attempt to modify empty component list' end - }), + }) --[=[@as evolved.component[]]=], - ---@type evolved.component[] __EMPTY_COMPONENT_STORAGE = __lua_setmetatable({}, { __tostring = function() return 'empty component storage' end, __newindex = function() __error_fmt 'attempt to modify empty component storage' end - }), + }) --[=[@as evolved.component[]]=], } --- @@ -991,12 +986,12 @@ function __primary_wildcard(secondary) local secondary_index = secondary % 2 ^ 20 if secondary_index == __ANY_INDEX then - return __WILDCARD_PAIR + return __ANY_WILDCARD end return primary_index + secondary_index * 2 ^ 20 - + __PRIMARY_WILDCARD_OPTS * 2 ^ 40 --[[@as evolved.id]] + + __PRI_WILDCARD_OPTS * 2 ^ 40 --[[@as evolved.id]] end ---@param primary evolved.id | integer id or index @@ -1007,12 +1002,12 @@ function __secondary_wildcard(primary) local secondary_index = __ANY_INDEX if primary_index == __ANY_INDEX then - return __WILDCARD_PAIR + return __ANY_WILDCARD end return primary_index + secondary_index * 2 ^ 20 - + __SECONDARY_WILDCARD_OPTS * 2 ^ 40 --[[@as evolved.id]] + + __SEC_WILDCARD_OPTS * 2 ^ 40 --[[@as evolved.id]] end ---@param fragment evolved.fragment @@ -1095,7 +1090,7 @@ function __iterator_fns.__execute_major_iterator(execute_state) __evolved_unpack(chunk_child_fragment) is_chunk_child_matched = - not exclude_set[__WILDCARD_PAIR] and + not exclude_set[__ANY_WILDCARD] and not exclude_set[__primary_wildcard(chunk_child_secondary_index)] and not exclude_set[__secondary_wildcard(chunk_child_primary_index)] end @@ -1400,7 +1395,7 @@ function __new_chunk(chunk_parent, chunk_fragment) __evolved_unpack(major) do - local major_wildcard = __WILDCARD_PAIR + local major_wildcard = __ANY_WILDCARD local major_wildcard_chunks = __major_chunks[major_wildcard] if not major_wildcard_chunks then @@ -1437,7 +1432,7 @@ function __new_chunk(chunk_parent, chunk_fragment) end if chunk_pair_count > 0 then - local minor_wildcard = __WILDCARD_PAIR + local minor_wildcard = __ANY_WILDCARD local minor_wildcard_chunks = __minor_chunks[minor_wildcard] if not minor_wildcard_chunks then @@ -1849,7 +1844,7 @@ local function __chunk_without_fragment(chunk, fragment) local primary_index, secondary_index, fragment_opts = __evolved_unpack(fragment) - if fragment_opts == __WILDCARD_OPTS then + if fragment_opts == __ANY_WILDCARD_OPTS then while chunk and chunk.__has_pair_major do chunk = chunk.__parent end @@ -1870,7 +1865,7 @@ local function __chunk_without_fragment(chunk, fragment) end return sib_chunk - elseif fragment_opts == __PRIMARY_WILDCARD_OPTS then + elseif fragment_opts == __PRI_WILDCARD_OPTS then if not chunk.__secondary_pairs[secondary_index] then -- the chunk does not have such pairs return chunk @@ -1901,7 +1896,7 @@ local function __chunk_without_fragment(chunk, fragment) end return sib_chunk - elseif fragment_opts == __SECONDARY_WILDCARD_OPTS then + elseif fragment_opts == __SEC_WILDCARD_OPTS then if not chunk.__primary_pairs[primary_index] then -- the chunk does not have such pairs return chunk @@ -2086,12 +2081,12 @@ local function __chunk_has_fragment(chunk, fragment) local primary_index, secondary_index, fragment_opts = __evolved_unpack(fragment) - if fragment_opts == __WILDCARD_OPTS then + if fragment_opts == __ANY_WILDCARD_OPTS then return true - elseif fragment_opts == __PRIMARY_WILDCARD_OPTS then + elseif fragment_opts == __PRI_WILDCARD_OPTS then local secondary_fragments = chunk.__secondary_pairs[secondary_index] return secondary_fragments and secondary_fragments.__item_count > 0 - elseif fragment_opts == __SECONDARY_WILDCARD_OPTS then + elseif fragment_opts == __SEC_WILDCARD_OPTS then local primary_fragments = chunk.__primary_pairs[primary_index] return primary_fragments and primary_fragments.__item_count > 0 end @@ -3022,7 +3017,7 @@ local function __purge_chunk(chunk) end if chunk.__has_pair_fragments then - local wildcard = __WILDCARD_PAIR + local wildcard = __ANY_WILDCARD local major_wildcard_chunks = __major_chunks[wildcard] local minor_wildcard_chunks = __minor_chunks[wildcard] @@ -3713,7 +3708,6 @@ function __chunk_remove(old_chunk, ...) end if old_chunk.__has_remove_hooks then - ---@type table local new_fragment_set = new_chunk and new_chunk.__fragment_set or __safe_tbls.__EMPTY_FRAGMENT_SET @@ -5373,7 +5367,6 @@ function __evolved_remove(entity, ...) local old_component_storages = old_chunk.__component_storages if old_chunk.__has_remove_hooks then - ---@type table local new_fragment_set = new_chunk and new_chunk.__fragment_set or __safe_tbls.__EMPTY_FRAGMENT_SET @@ -6115,11 +6108,11 @@ function __evolved_pair(primary, secondary) local options = __PAIR_OPTS if primary == __ANY and secondary == __ANY then - options = __WILDCARD_OPTS + options = __ANY_WILDCARD_OPTS elseif primary == __ANY then - options = __PRIMARY_WILDCARD_OPTS + options = __PRI_WILDCARD_OPTS elseif secondary == __ANY then - options = __SECONDARY_WILDCARD_OPTS + options = __SEC_WILDCARD_OPTS end return primary % 2 ^ 20 @@ -6578,13 +6571,13 @@ function __builder_mt:has(fragment) local fragment_primary_index, fragment_secondary_index, fragment_opts = __evolved_unpack(fragment) - if fragment_opts == __WILDCARD_OPTS then + if fragment_opts == __ANY_WILDCARD_OPTS then return __lua_next(primary_pairs) ~= nil and __lua_next(secondary_pairs) ~= nil - elseif fragment_opts == __PRIMARY_WILDCARD_OPTS then + elseif fragment_opts == __PRI_WILDCARD_OPTS then local secondary_fragments = secondary_pairs[fragment_secondary_index] return secondary_fragments ~= nil and secondary_fragments.__item_count > 0 - elseif fragment_opts == __SECONDARY_WILDCARD_OPTS then + elseif fragment_opts == __SEC_WILDCARD_OPTS then local primary_fragments = primary_pairs[fragment_primary_index] return primary_fragments ~= nil and primary_fragments.__item_count > 0 end @@ -6823,7 +6816,7 @@ function __builder_mt:remove(...) local fragment_primary_index, fragment_secondary_index, fragment_opts = __evolved_unpack(fragment) - if fragment_opts == __WILDCARD_OPTS then + if fragment_opts == __ANY_WILDCARD_OPTS then for primary_index, primary_fragments in __lua_next, primary_pairs do local primary_fragment_list = primary_fragments.__item_list local primary_fragment_count = primary_fragments.__item_count @@ -6847,7 +6840,7 @@ function __builder_mt:remove(...) secondary_pairs[secondary_index] = nil end - elseif fragment_opts == __PRIMARY_WILDCARD_OPTS then + elseif fragment_opts == __PRI_WILDCARD_OPTS then local secondary_fragments = secondary_pairs[fragment_secondary_index] local secondary_fragment_list = secondary_fragments and secondary_fragments.__item_list local secondary_fragment_count = secondary_fragments and secondary_fragments.__item_count or 0 @@ -6863,7 +6856,7 @@ function __builder_mt:remove(...) end secondary_pairs[fragment_secondary_index] = nil - elseif fragment_opts == __SECONDARY_WILDCARD_OPTS then + elseif fragment_opts == __SEC_WILDCARD_OPTS then local primary_fragments = primary_pairs[fragment_primary_index] local primary_fragment_list = primary_fragments and primary_fragments.__item_list local primary_fragment_count = primary_fragments and primary_fragments.__item_count or 0 From 71a7d382c12e4315b15f5477f89af043f337962b Mon Sep 17 00:00:00 2001 From: BlackMATov Date: Mon, 25 Aug 2025 18:34:34 +0700 Subject: [PATCH 03/12] more type annots --- README.md | 21 ++--- develop/testing/pairs_tests.lua | 1 + evolved.lua | 140 ++++++++++++++++++-------------- 3 files changed, 91 insertions(+), 71 deletions(-) diff --git a/README.md b/README.md index e2358b5..164000b 100644 --- a/README.md +++ b/README.md @@ -148,11 +148,11 @@ function evolved.alive_any(...) end Sometimes (for debugging purposes, for example), it is necessary to extract the index and version from an identifier or to pack them back into an identifier. The [`evolved.pack`](#evolvedpack) and [`evolved.unpack`](#evolvedunpack) functions can be used for this purpose. ```lua ----@param primary integer ----@param secondary integer +---@param index integer +---@param version integer ---@return evolved.id id ---@nodiscard -function evolved.pack(primary, secondary) end +function evolved.pack(index, version) end ---@param id evolved.id ---@return integer primary @@ -1026,6 +1026,7 @@ assert(not evolved.alive(entity)) ``` id :: implementation-specific +pair :: id entity :: id fragment :: id @@ -1149,8 +1150,8 @@ collect_garbage :: () ### Relation Functions ``` -pair :: id, id -> id -unpair :: id -> id, id +pair :: id, id -> pair +unpair :: pair -> id, id is_pair :: id -> boolean is_wildcard :: id -> boolean @@ -1332,11 +1333,11 @@ function evolved.name(...) end ### `evolved.pack` ```lua ----@param primary integer ----@param secondary integer +---@param index integer +---@param version integer ---@return evolved.id id ---@nodiscard -function evolved.pack(primary, secondary) end +function evolved.pack(index, version) end ``` ### `evolved.unpack` @@ -1584,7 +1585,7 @@ function evolved.collect_garbage() end ```lua ---@param primary evolved.id ---@param secondary evolved.id ----@return evolved.id pair +---@return evolved.pair pair ---@nodiscard function evolved.pair(primary, secondary) end ``` @@ -1592,7 +1593,7 @@ function evolved.pair(primary, secondary) end ### `evolved.unpair` ```lua ----@param pair evolved.id +---@param pair evolved.pair ---@return evolved.id primary ---@return evolved.id secondary ---@nodiscard diff --git a/develop/testing/pairs_tests.lua b/develop/testing/pairs_tests.lua index 58ed05d..dcc8435 100644 --- a/develop/testing/pairs_tests.lua +++ b/develop/testing/pairs_tests.lua @@ -1617,3 +1617,4 @@ end -- process evo.ANY as single wildcard -- should we provide an evolved.pair type? -- how should the destroy function handle pairs? +-- add debug mode for pack/unpack and pair/unpair diff --git a/evolved.lua b/evolved.lua index 4ad5de0..a307be5 100644 --- a/evolved.lua +++ b/evolved.lua @@ -28,6 +28,7 @@ local evolved = { } ---@class evolved.id +---@alias evolved.pair evolved.id ---@alias evolved.entity evolved.id ---@alias evolved.fragment evolved.id @@ -153,8 +154,8 @@ local __defer_length = 0 ---@type integer local __defer_bytecode = {} ---@type any[] local __root_chunks = {} ---@type table -local __major_chunks = {} ---@type table -local __minor_chunks = {} ---@type table +local __major_chunks = {} ---@type table> +local __minor_chunks = {} ---@type table> local __pinned_chunks = {} ---@type table @@ -163,11 +164,11 @@ local __entity_places = {} ---@type table local __structural_changes = 0 ---@type integer -local __sorted_includes = {} ---@type table -local __sorted_excludes = {} ---@type table -local __sorted_requires = {} ---@type table +local __sorted_includes = {} ---@type table> +local __sorted_excludes = {} ---@type table> +local __sorted_requires = {} ---@type table> -local __group_subsystems = {} ---@type table +local __group_subsystems = {} ---@type table> --- --- @@ -190,10 +191,10 @@ local __group_subsystems = {} ---@type table ---@field package __component_indices table ---@field package __component_storages evolved.storage[] ---@field package __component_fragments evolved.fragment[] ----@field package __pair_list evolved.id[] +---@field package __pair_list evolved.pair[] ---@field package __pair_count integer ----@field package __primary_pairs table ----@field package __secondary_pairs table +---@field package __primary_pairs table> +---@field package __secondary_pairs table> ---@field package __with_fragment_edges table ---@field package __without_fragment_edges table ---@field package __unreachable_or_collected boolean @@ -219,8 +220,8 @@ __chunk_mt.__index = __chunk_mt ---@class evolved.builder ---@field package __components table ----@field package __primary_pairs? table ----@field package __secondary_pairs? table +---@field package __primary_pairs? table> +---@field package __secondary_pairs? table> local __builder_mt = {} __builder_mt.__index = __builder_mt @@ -578,10 +579,11 @@ end --- --- ----@class (exact) evolved.assoc_list ----@field package __item_set table ----@field package __item_list any[] ----@field package __item_count integer +---@class (exact) evolved.assoc_list: { +--- __item_set: { [K]: integer }, +--- __item_list: K[], +--- __item_count: integer, +--- } local __assoc_list_new local __assoc_list_dup @@ -826,7 +828,7 @@ local __ANY_INDEX = __ANY % 2 ^ 20 --[[@as integer]] local __ANY_WILDCARD = __ANY_INDEX + __ANY_INDEX * 2 ^ 20 - + __ANY_WILDCARD_OPTS * 2 ^ 40 --[[@as evolved.id]] + + __ANY_WILDCARD_OPTS * 2 ^ 40 --[[@as evolved.pair]] --- --- @@ -979,7 +981,7 @@ function __id_name(id) end ---@param secondary evolved.id | integer id or index ----@return evolved.id pair (*, secondary) +---@return evolved.pair (*, secondary) ---@nodiscard function __primary_wildcard(secondary) local primary_index = __ANY_INDEX @@ -991,11 +993,11 @@ function __primary_wildcard(secondary) return primary_index + secondary_index * 2 ^ 20 - + __PRI_WILDCARD_OPTS * 2 ^ 40 --[[@as evolved.id]] + + __PRI_WILDCARD_OPTS * 2 ^ 40 --[[@as evolved.pair]] end ---@param primary evolved.id | integer id or index ----@return evolved.id pair (primary, *) +---@return evolved.pair (primary, *) ---@nodiscard function __secondary_wildcard(primary) local primary_index = primary % 2 ^ 20 @@ -1007,7 +1009,7 @@ function __secondary_wildcard(primary) return primary_index + secondary_index * 2 ^ 20 - + __SEC_WILDCARD_OPTS * 2 ^ 40 --[[@as evolved.id]] + + __SEC_WILDCARD_OPTS * 2 ^ 40 --[[@as evolved.pair]] end ---@param fragment evolved.fragment @@ -1238,11 +1240,11 @@ function __new_chunk(chunk_parent, chunk_fragment) local chunk_fragment_list = {} ---@type evolved.fragment[] local chunk_fragment_count = 0 ---@type integer - local chunk_pair_list = {} ---@type evolved.id[] + local chunk_pair_list = {} ---@type evolved.pair[] local chunk_pair_count = 0 ---@type integer - local chunk_primary_pairs = {} ---@type table - local chunk_secondary_pairs = {} ---@type table + local chunk_primary_pairs = {} ---@type table> + local chunk_secondary_pairs = {} ---@type table> if chunk_parent then do @@ -1291,11 +1293,13 @@ function __new_chunk(chunk_parent, chunk_fragment) local chunk_secondary_fragments = chunk_secondary_pairs[chunk_secondary_index] if not chunk_primary_fragments then + ---@type evolved.assoc_list chunk_primary_fragments = __assoc_list_new(1) chunk_primary_pairs[chunk_primary_index] = chunk_primary_fragments end if not chunk_secondary_fragments then + ---@type evolved.assoc_list chunk_secondary_fragments = __assoc_list_new(1) chunk_secondary_pairs[chunk_secondary_index] = chunk_secondary_fragments end @@ -1370,6 +1374,7 @@ function __new_chunk(chunk_parent, chunk_fragment) local major_chunks = __major_chunks[major] if not major_chunks then + ---@type evolved.assoc_list major_chunks = __assoc_list_new(4) __major_chunks[major] = major_chunks end @@ -1382,6 +1387,7 @@ function __new_chunk(chunk_parent, chunk_fragment) local minor_chunks = __minor_chunks[minor] if not minor_chunks then + ---@type evolved.assoc_list minor_chunks = __assoc_list_new(4) __minor_chunks[minor] = minor_chunks end @@ -1399,6 +1405,7 @@ function __new_chunk(chunk_parent, chunk_fragment) local major_wildcard_chunks = __major_chunks[major_wildcard] if not major_wildcard_chunks then + ---@type evolved.assoc_list major_wildcard_chunks = __assoc_list_new(4) __major_chunks[major_wildcard] = major_wildcard_chunks end @@ -1411,6 +1418,7 @@ function __new_chunk(chunk_parent, chunk_fragment) local major_wildcard_chunks = __major_chunks[major_wildcard] if not major_wildcard_chunks then + ---@type evolved.assoc_list major_wildcard_chunks = __assoc_list_new(4) __major_chunks[major_wildcard] = major_wildcard_chunks end @@ -1423,6 +1431,7 @@ function __new_chunk(chunk_parent, chunk_fragment) local major_wildcard_chunks = __major_chunks[major_wildcard] if not major_wildcard_chunks then + ---@type evolved.assoc_list major_wildcard_chunks = __assoc_list_new(4) __major_chunks[major_wildcard] = major_wildcard_chunks end @@ -1436,6 +1445,7 @@ function __new_chunk(chunk_parent, chunk_fragment) local minor_wildcard_chunks = __minor_chunks[minor_wildcard] if not minor_wildcard_chunks then + ---@type evolved.assoc_list minor_wildcard_chunks = __assoc_list_new(4) __minor_chunks[minor_wildcard] = minor_wildcard_chunks end @@ -1453,6 +1463,7 @@ function __new_chunk(chunk_parent, chunk_fragment) local minor_wildcard_chunks = __minor_chunks[minor_wildcard] if not minor_wildcard_chunks then + ---@type evolved.assoc_list minor_wildcard_chunks = __assoc_list_new(4) __minor_chunks[minor_wildcard] = minor_wildcard_chunks end @@ -1465,6 +1476,7 @@ function __new_chunk(chunk_parent, chunk_fragment) local minor_wildcard_chunks = __minor_chunks[minor_wildcard] if not minor_wildcard_chunks then + ---@type evolved.assoc_list minor_wildcard_chunks = __assoc_list_new(4) __minor_chunks[minor_wildcard] = minor_wildcard_chunks end @@ -1617,8 +1629,8 @@ function __trace_major_chunks(major, trace, ...) do local major_chunks = __major_chunks[major] - local major_chunk_list = major_chunks and major_chunks.__item_list --[=[@as evolved.chunk[]]=] - local major_chunk_count = major_chunks and major_chunks.__item_count or 0 --[[@as integer]] + local major_chunk_list = major_chunks and major_chunks.__item_list + local major_chunk_count = major_chunks and major_chunks.__item_count or 0 if major_chunk_count > 0 then __lua_table_move( @@ -1631,8 +1643,8 @@ function __trace_major_chunks(major, trace, ...) do local major_chunks = __major_chunks[__primary_wildcard(major)] - local major_chunk_list = major_chunks and major_chunks.__item_list --[=[@as evolved.chunk[]]=] - local major_chunk_count = major_chunks and major_chunks.__item_count or 0 --[[@as integer]] + local major_chunk_list = major_chunks and major_chunks.__item_list + local major_chunk_count = major_chunks and major_chunks.__item_count or 0 if major_chunk_count > 0 then __lua_table_move( @@ -1645,8 +1657,8 @@ function __trace_major_chunks(major, trace, ...) do local major_chunks = __major_chunks[__secondary_wildcard(major)] - local major_chunk_list = major_chunks and major_chunks.__item_list --[=[@as evolved.chunk[]]=] - local major_chunk_count = major_chunks and major_chunks.__item_count or 0 --[[@as integer]] + local major_chunk_list = major_chunks and major_chunks.__item_list + local major_chunk_count = major_chunks and major_chunks.__item_count or 0 if major_chunk_count > 0 then __lua_table_move( @@ -1694,8 +1706,8 @@ function __trace_minor_chunks(minor, trace, ...) do local minor_chunks = __minor_chunks[minor] - local minor_chunk_list = minor_chunks and minor_chunks.__item_list --[=[@as evolved.chunk[]]=] - local minor_chunk_count = minor_chunks and minor_chunks.__item_count or 0 --[[@as integer]] + local minor_chunk_list = minor_chunks and minor_chunks.__item_list + local minor_chunk_count = minor_chunks and minor_chunks.__item_count or 0 if minor_chunk_count > 0 then __lua_table_move( @@ -1708,8 +1720,8 @@ function __trace_minor_chunks(minor, trace, ...) do local minor_chunks = __minor_chunks[__primary_wildcard(minor)] - local minor_chunk_list = minor_chunks and minor_chunks.__item_list --[=[@as evolved.chunk[]]=] - local minor_chunk_count = minor_chunks and minor_chunks.__item_count or 0 --[[@as integer]] + local minor_chunk_list = minor_chunks and minor_chunks.__item_list + local minor_chunk_count = minor_chunks and minor_chunks.__item_count or 0 if minor_chunk_count > 0 then __lua_table_move( @@ -1722,8 +1734,8 @@ function __trace_minor_chunks(minor, trace, ...) do local minor_chunks = __minor_chunks[__secondary_wildcard(minor)] - local minor_chunk_list = minor_chunks and minor_chunks.__item_list --[=[@as evolved.chunk[]]=] - local minor_chunk_count = minor_chunks and minor_chunks.__item_count or 0 --[[@as integer]] + local minor_chunk_list = minor_chunks and minor_chunks.__item_list + local minor_chunk_count = minor_chunks and minor_chunks.__item_count or 0 if minor_chunk_count > 0 then __lua_table_move( @@ -2365,8 +2377,8 @@ local function __chunk_required_fragments(chunk, req_fragment_set, req_fragment_ end local fragment_requires = __sorted_requires[stack_fragment] - local fragment_require_list = fragment_requires and fragment_requires.__item_list --[=[@as evolved.fragment[]]=] - local fragment_require_count = fragment_requires and fragment_requires.__item_count or 0 --[[@as integer]] + local fragment_require_list = fragment_requires and fragment_requires.__item_list + local fragment_require_count = fragment_requires and fragment_requires.__item_count or 0 for fragment_require_index = 1, fragment_require_count do local required_fragment = fragment_require_list[fragment_require_index] @@ -2415,8 +2427,8 @@ local function __fragment_required_fragments(fragment, req_fragment_set, req_fra end local fragment_requires = __sorted_requires[stack_fragment] - local fragment_require_list = fragment_requires and fragment_requires.__item_list --[=[@as evolved.fragment[]]=] - local fragment_require_count = fragment_requires and fragment_requires.__item_count or 0 --[[@as integer]] + local fragment_require_list = fragment_requires and fragment_requires.__item_list + local fragment_require_count = fragment_requires and fragment_requires.__item_count or 0 for fragment_require_index = 1, fragment_require_count do local required_fragment = fragment_require_list[fragment_require_index] @@ -3907,8 +3919,8 @@ local function __system_process(system) do local group_subsystems = __group_subsystems[system] - local group_subsystem_list = group_subsystems and group_subsystems.__item_list --[=[@as evolved.system[]]=] - local group_subsystem_count = group_subsystems and group_subsystems.__item_count or 0 --[[@as integer]] + local group_subsystem_list = group_subsystems and group_subsystems.__item_list + local group_subsystem_count = group_subsystems and group_subsystems.__item_count or 0 if group_subsystem_count > 0 then local subsystem_list = __acquire_table(__table_pool_tag.system_list) @@ -4666,13 +4678,13 @@ function __evolved_name(...) end end ----@param primary integer ----@param secondary integer +---@param index integer +---@param version integer ---@return evolved.id id ---@nodiscard -function __evolved_pack(primary, secondary) - return primary % 2 ^ 20 - + secondary % 2 ^ 20 * 2 ^ 20 --[[@as evolved.id]] +function __evolved_pack(index, version) + return index % 2 ^ 20 + + version % 2 ^ 20 * 2 ^ 20 --[[@as evolved.id]] end ---@param id evolved.id @@ -5834,29 +5846,29 @@ function __evolved_execute(query) local chunk_stack_size = 0 local query_includes = __sorted_includes[query] - local query_include_set = query_includes and query_includes.__item_set --[[@as table]] - local query_include_list = query_includes and query_includes.__item_list --[=[@as evolved.fragment[]]=] - local query_include_count = query_includes and query_includes.__item_count or 0 --[[@as integer]] + local query_include_set = query_includes and query_includes.__item_set + local query_include_list = query_includes and query_includes.__item_list + local query_include_count = query_includes and query_includes.__item_count or 0 local query_excludes = __sorted_excludes[query] - local query_exclude_set = query_excludes and query_excludes.__item_set --[[@as table]] - local query_exclude_list = query_excludes and query_excludes.__item_list --[=[@as evolved.fragment[]]=] - local query_exclude_count = query_excludes and query_excludes.__item_count or 0 --[[@as integer]] + local query_exclude_set = query_excludes and query_excludes.__item_set + local query_exclude_list = query_excludes and query_excludes.__item_list + local query_exclude_count = query_excludes and query_excludes.__item_count or 0 if query_include_count > 0 then local query_major = query_include_list[query_include_count] if __evolved_is_wildcard(query_major) then local minor_chunks = __minor_chunks[query_major] - local minor_chunk_list = minor_chunks and minor_chunks.__item_list --[=[@as evolved.chunk[]]=] - local minor_chunk_count = minor_chunks and minor_chunks.__item_count or 0 --[[@as integer]] + local minor_chunk_list = minor_chunks and minor_chunks.__item_list + local minor_chunk_count = minor_chunks and minor_chunks.__item_count or 0 for query_include_index = 1, query_include_count - 1 do local query_minor = query_include_list[query_include_index] local query_chunks = __minor_chunks[query_minor] - local query_chunk_list = query_chunks and query_chunks.__item_list --[=[@as evolved.chunk[]]=] - local query_chunk_count = query_chunks and query_chunks.__item_count or 0 --[[@as integer]] + local query_chunk_list = query_chunks and query_chunks.__item_list + local query_chunk_count = query_chunks and query_chunks.__item_count or 0 if query_chunk_count < minor_chunk_count then minor_chunks, minor_chunk_list, minor_chunk_count = @@ -5922,8 +5934,8 @@ function __evolved_execute(query) return __iterator_fns.__execute_minor_iterator, execute_state else local major_chunks = __major_chunks[query_major] - local major_chunk_list = major_chunks and major_chunks.__item_list --[=[@as evolved.chunk[]]=] - local major_chunk_count = major_chunks and major_chunks.__item_count or 0 --[[@as integer]] + local major_chunk_list = major_chunks and major_chunks.__item_list + local major_chunk_count = major_chunks and major_chunks.__item_count or 0 for major_chunk_index = 1, major_chunk_count do local major_chunk = major_chunk_list[major_chunk_index] @@ -6102,7 +6114,7 @@ end ---@param primary evolved.id ---@param secondary evolved.id ----@return evolved.id pair +---@return evolved.pair pair ---@nodiscard function __evolved_pair(primary, secondary) local options = __PAIR_OPTS @@ -6117,10 +6129,10 @@ function __evolved_pair(primary, secondary) return primary % 2 ^ 20 + secondary % 2 ^ 20 * 2 ^ 20 - + options * 2 ^ 40 --[[@as evolved.id]] + + options * 2 ^ 40 --[[@as evolved.pair]] end ----@param pair evolved.id +---@param pair evolved.pair ---@return evolved.id primary ---@return evolved.id secondary ---@nodiscard @@ -6773,11 +6785,13 @@ function __builder_mt:set(fragment, component) local secondary_fragments = secondary_pairs[fragment_secondary_index] if not primary_fragments then + ---@type evolved.assoc_list primary_fragments = __assoc_list_new(4) primary_pairs[fragment_primary_index] = primary_fragments end if not secondary_fragments then + ---@type evolved.assoc_list secondary_fragments = __assoc_list_new(4) secondary_pairs[fragment_secondary_index] = secondary_fragments end @@ -7261,6 +7275,7 @@ __evolved_set(__INCLUDES, __ON_SET, function(query, _, include_list) return end + ---@type evolved.assoc_list local sorted_includes = __assoc_list_new(include_count) __assoc_list_move(include_list, 1, include_count, sorted_includes) @@ -7289,6 +7304,7 @@ __evolved_set(__EXCLUDES, __ON_SET, function(query, _, exclude_list) return end + ---@type evolved.assoc_list local sorted_excludes = __assoc_list_new(exclude_count) __assoc_list_move(exclude_list, 1, exclude_count, sorted_excludes) @@ -7317,6 +7333,7 @@ __evolved_set(__REQUIRES, __ON_SET, function(fragment, _, require_list) return end + ---@type evolved.assoc_list local sorted_requires = __assoc_list_new(require_count) __assoc_list_move(require_list, 1, require_count, sorted_requires) @@ -7354,6 +7371,7 @@ __evolved_set(__GROUP, __ON_SET, function(system, _, new_group, old_group) local new_group_systems = __group_subsystems[new_group] if not new_group_systems then + ---@type evolved.assoc_list new_group_systems = __assoc_list_new(4) __group_subsystems[new_group] = new_group_systems end From 12beee6eecf8dd6ec33815e56ceb1a8bacdd84da Mon Sep 17 00:00:00 2001 From: BlackMATov Date: Tue, 26 Aug 2025 05:42:04 +0700 Subject: [PATCH 04/12] debug mode for pack/unpack and pair/unpair --- develop/fuzzing/pack_unpack_fuzz.lua | 12 ---- develop/fuzzing/wildcard_fuzz.lua | 17 ++++- develop/testing/pairs_tests.lua | 27 ++++++-- evolved.lua | 97 ++++++++++++++++++---------- 4 files changed, 98 insertions(+), 55 deletions(-) diff --git a/develop/fuzzing/pack_unpack_fuzz.lua b/develop/fuzzing/pack_unpack_fuzz.lua index 32c1997..b1dce49 100644 --- a/develop/fuzzing/pack_unpack_fuzz.lua +++ b/develop/fuzzing/pack_unpack_fuzz.lua @@ -19,15 +19,3 @@ for _ = 1, 1000 do assert(initial_secondary == unpacked_secondary) assert(0 == unpacked_options) end - -for _ = 1, 1000 do - local initial_primary = math.random(1, 2 ^ 31 - 1) - local initial_secondary = math.random(1, 2 ^ 31 - 1) - - local packed_id = evo.pack(initial_primary, initial_secondary) - local unpacked_primary, unpacked_secondary, unpacked_options = evo.unpack(packed_id) - - assert(initial_primary % 2 ^ 20 == unpacked_primary) - assert(initial_secondary % 2 ^ 20 == unpacked_secondary) - assert(0 == unpacked_options) -end diff --git a/develop/fuzzing/wildcard_fuzz.lua b/develop/fuzzing/wildcard_fuzz.lua index 916afc2..0943a8d 100644 --- a/develop/fuzzing/wildcard_fuzz.lua +++ b/develop/fuzzing/wildcard_fuzz.lua @@ -180,9 +180,20 @@ for _ = 1, math.random(1, 100) do for fragment in evo.each(entity) do if evo.has(fragment, evo.EXPLICIT) then - local is_fragment_included = - query_include_set[fragment] ~= nil or - query_include_set[evo.pair(fragment, evo.ANY)] ~= nil + local is_fragment_included = false + + if not is_fragment_included then + is_fragment_included = query_include_set[fragment] ~= nil + end + + if not is_fragment_included and evo.is_pair(fragment) then + local fragment_primary = evo.unpair(fragment) + is_fragment_included = query_include_set[evo.pair(fragment_primary, evo.ANY)] ~= nil + end + + if not is_fragment_included and not evo.is_pair(fragment) then + is_fragment_included = query_include_set[evo.pair(fragment, evo.ANY)] ~= nil + end if not is_fragment_included then is_entity_expected = false diff --git a/develop/testing/pairs_tests.lua b/develop/testing/pairs_tests.lua index dcc8435..f907b89 100644 --- a/develop/testing/pairs_tests.lua +++ b/develop/testing/pairs_tests.lua @@ -1,10 +1,28 @@ local evo = require 'evolved' do - local p1, s1 = evo.id(2) - local pair1 = evo.pair(p1, s1) - local p2, s2 = evo.unpair(pair1) - assert(p1 == p2 and s1 == s2) + do + local p, s = evo.unpair(evo.pair(evo.ANY, evo.ANY)) + assert(p == evo.ANY and s == evo.ANY) + end + + do + local f = evo.id() + local p, s = evo.unpair(evo.pair(evo.ANY, f)) + assert(p == evo.ANY and s == f) + end + + do + local f = evo.id() + local p, s = evo.unpair(evo.pair(f, evo.ANY)) + assert(p == f and s == evo.ANY) + end + + do + local fp, fs = evo.id(2) + local p, s = evo.unpair(evo.pair(fp, fs)) + assert(p == fp and s == fs) + end end do @@ -1617,4 +1635,3 @@ end -- process evo.ANY as single wildcard -- should we provide an evolved.pair type? -- how should the destroy function handle pairs? --- add debug mode for pack/unpack and pair/unpair diff --git a/evolved.lua b/evolved.lua index a307be5..8fa86dc 100644 --- a/evolved.lua +++ b/evolved.lua @@ -948,7 +948,9 @@ local __component_storage ---@return string ---@nodiscard function __id_name(id) - if not __evolved_is_pair(id) then + local id_primary, id_secondary, id_options = __evolved_unpack(id) + + if id_options < __PAIR_OPTS then ---@type string? local id_name = __evolved_get(id, __NAME) @@ -956,27 +958,24 @@ function __id_name(id) return id_name end else - local id_primary_index, id_secondary_index = __evolved_unpack(id) + ---@type string?, string? + local pair_primary_id_name, pair_secondary_id_name - local id_primary = __freelist_ids[id_primary_index] --[[@as evolved.id?]] - local id_secondary = __freelist_ids[id_secondary_index] --[[@as evolved.id?]] - - local id_primary_name, id_secondary_name - - if id_primary and id_primary % 2 ^ 20 == id_primary_index then - id_primary_name = __id_name(id_primary) + local pair_primary_id = __freelist_ids[id_primary] --[[@as evolved.id?]] + if pair_primary_id and pair_primary_id % 2 ^ 20 == id_primary then + pair_primary_id_name = __id_name(pair_primary_id) end - if id_secondary and id_secondary % 2 ^ 20 == id_secondary_index then - id_secondary_name = __id_name(id_secondary) + local pair_secondary_id = __freelist_ids[id_secondary] --[[@as evolved.id?]] + if pair_secondary_id and pair_secondary_id % 2 ^ 20 == id_secondary then + pair_secondary_id_name = __id_name(pair_secondary_id) end - if id_primary_name and id_secondary_name then - return __lua_string_format('${%s,%s}', id_primary_name, id_secondary_name) + if pair_primary_id_name and pair_secondary_id_name then + return __lua_string_format('${%s,%s}', pair_primary_id_name, pair_secondary_id_name) end end - local id_primary, id_secondary, id_options = __evolved_unpack(id) return __lua_string_format('$%d#%d:%d:%d', id, id_primary, id_secondary, id_options) end @@ -4683,8 +4682,16 @@ end ---@return evolved.id id ---@nodiscard function __evolved_pack(index, version) - return index % 2 ^ 20 - + version % 2 ^ 20 * 2 ^ 20 --[[@as evolved.id]] + if index < 1 or index > 2 ^ 20 - 1 then + __error_fmt('index (%d) is out of range [1, 2 ^ 20 - 1]', index) + end + + if version < 1 or version > 2 ^ 20 - 1 then + __error_fmt('version (%d) is out of range [1, 2 ^ 20 - 1]', version) + end + + return index + + version * 2 ^ 20 --[[@as evolved.id]] end ---@param id evolved.id @@ -6117,19 +6124,31 @@ end ---@return evolved.pair pair ---@nodiscard function __evolved_pair(primary, secondary) - local options = __PAIR_OPTS - - if primary == __ANY and secondary == __ANY then - options = __ANY_WILDCARD_OPTS - elseif primary == __ANY then - options = __PRI_WILDCARD_OPTS - elseif secondary == __ANY then - options = __SEC_WILDCARD_OPTS + local primary_index, _, primary_options = __evolved_unpack(primary) + if primary_options >= __PAIR_OPTS then + __error_fmt('the primary id (%s) is a pair and cannot be used as a primary id of a pair', + __id_name(primary)) end - return primary % 2 ^ 20 - + secondary % 2 ^ 20 * 2 ^ 20 - + options * 2 ^ 40 --[[@as evolved.pair]] + local secondary_index, _, secondary_options = __evolved_unpack(secondary) + if secondary_options >= __PAIR_OPTS then + __error_fmt('the secondary id (%s) is a pair and cannot be used as a secondary id of a pair', + __id_name(secondary)) + end + + local pair_options = __PAIR_OPTS + + if primary == __ANY and secondary == __ANY then + pair_options = __ANY_WILDCARD_OPTS + elseif primary == __ANY then + pair_options = __PRI_WILDCARD_OPTS + elseif secondary == __ANY then + pair_options = __SEC_WILDCARD_OPTS + end + + return primary_index + + secondary_index * 2 ^ 20 + + pair_options * 2 ^ 40 --[[@as evolved.pair]] end ---@param pair evolved.pair @@ -6137,19 +6156,27 @@ end ---@return evolved.id secondary ---@nodiscard function __evolved_unpair(pair) - local primary_index = pair % 2 ^ 20 - local secondary_index = (pair - primary_index) / 2 ^ 20 % 2 ^ 20 + local pair_primary, pair_secondary, pair_options = __evolved_unpack(pair) - local primary = __freelist_ids[primary_index] --[[@as evolved.id]] - if not primary or primary % 2 ^ 20 ~= primary_index then - __error_fmt('the primary fragment of the pair (%s) is not alive and cannot be unpaired', + if pair_options < __PAIR_OPTS then + __error_fmt('the id (%s) is not a pair and cannot be unpaired', __id_name(pair)) end - local secondary = __freelist_ids[secondary_index] --[[@as evolved.id]] - if not secondary or secondary % 2 ^ 20 ~= secondary_index then - __error_fmt('the secondary fragment of the pair (%s) is not alive and cannot be unpaired', + local primary = __freelist_ids[pair_primary] --[[@as evolved.id?]] + if not primary or primary % 2 ^ 20 ~= pair_primary then + __error_fmt('the primary id of the pair (%s) is not alive and cannot be unpaired', __id_name(pair)) + else + ---@cast primary -? + end + + local secondary = __freelist_ids[pair_secondary] --[[@as evolved.id?]] + if not secondary or secondary % 2 ^ 20 ~= pair_secondary then + __error_fmt('the secondary id of the pair (%s) is not alive and cannot be unpaired', + __id_name(pair)) + else + ---@cast secondary -? end return primary, secondary From d4a7c7b77c61827cf01654401ff825f1f38e5312 Mon Sep 17 00:00:00 2001 From: BlackMATov Date: Fri, 29 Aug 2025 05:56:23 +0700 Subject: [PATCH 05/12] more pair checks --- develop/fuzzing/wildcard_fuzz.lua | 12 +- develop/testing/pairs_tests.lua | 83 +++++-- evolved.lua | 401 +++++++++++++++--------------- 3 files changed, 278 insertions(+), 218 deletions(-) diff --git a/develop/fuzzing/wildcard_fuzz.lua b/develop/fuzzing/wildcard_fuzz.lua index 0943a8d..9a64fc6 100644 --- a/develop/fuzzing/wildcard_fuzz.lua +++ b/develop/fuzzing/wildcard_fuzz.lua @@ -179,7 +179,17 @@ for _ = 1, math.random(1, 100) do not evo.has_any(entity, __table_unpack(query_exclude_list)) for fragment in evo.each(entity) do - if evo.has(fragment, evo.EXPLICIT) then + local is_fragment_explicit = false + + if not is_fragment_explicit and evo.is_pair(fragment) then + is_fragment_explicit = evo.has(evo.unpair(fragment), evo.EXPLICIT) + end + + if not is_fragment_explicit and not evo.is_pair(fragment) then + is_fragment_explicit = evo.has(fragment, evo.EXPLICIT) + end + + if is_fragment_explicit then local is_fragment_included = false if not is_fragment_included then diff --git a/develop/testing/pairs_tests.lua b/develop/testing/pairs_tests.lua index f907b89..bdfd3c7 100644 --- a/develop/testing/pairs_tests.lua +++ b/develop/testing/pairs_tests.lua @@ -91,20 +91,20 @@ do assert(evo.has_any(e12, evo.pair(p1, evo.ANY), evo.pair(p2, evo.ANY))) end -do - local p1, s1, p2, s2 = evo.id(4) - evo.set(p1, s1) - evo.set(s1, p1) - evo.set(p2, s2) - assert(not evo.empty(evo.pair(p1, s1))) - assert(not evo.empty(evo.pair(p2, s2))) - assert(not evo.empty_all(evo.pair(p1, s1), evo.pair(p2, s2))) - assert(not evo.empty_any(evo.pair(p1, s1), evo.pair(p2, s2))) - assert(not evo.empty_all(evo.pair(p1, s1), evo.pair(p2, s2), p1)) - assert(not evo.empty_any(evo.pair(p1, s1), evo.pair(p2, s2), p1)) - assert(not evo.empty_all(evo.pair(p1, s1), evo.pair(p2, s2), s2)) - assert(evo.empty_any(evo.pair(p1, s1), evo.pair(p2, s2), s2)) -end +-- do +-- local p1, s1, p2, s2 = evo.id(4) +-- evo.set(p1, s1) +-- evo.set(s1, p1) +-- evo.set(p2, s2) +-- assert(not evo.empty(evo.pair(p1, s1))) +-- assert(not evo.empty(evo.pair(p2, s2))) +-- assert(not evo.empty_all(evo.pair(p1, s1), evo.pair(p2, s2))) +-- assert(not evo.empty_any(evo.pair(p1, s1), evo.pair(p2, s2))) +-- assert(not evo.empty_all(evo.pair(p1, s1), evo.pair(p2, s2), p1)) +-- assert(not evo.empty_any(evo.pair(p1, s1), evo.pair(p2, s2), p1)) +-- assert(not evo.empty_all(evo.pair(p1, s1), evo.pair(p2, s2), s2)) +-- assert(evo.empty_any(evo.pair(p1, s1), evo.pair(p2, s2), s2)) +-- end do local p1, s1 = evo.id(2) @@ -1628,6 +1628,61 @@ do assert(evo.name(evo.pair(evo.ANY, evo.ANY)) == '${ANY,ANY}') end +do + do + local p, s = evo.id(2) + assert(evo.alive(evo.pair(p, s))) + end + + do + local p, s = evo.id(2) + evo.destroy(p) + assert(not evo.alive(evo.pair(p, s))) + end + + do + local p, s = evo.id(2) + evo.destroy(s) + assert(evo.alive(evo.pair(p, s))) + end + + do + local p, s = evo.id(2) + evo.destroy(p, s) + assert(not evo.alive(evo.pair(p, s))) + end +end + +do + do + local p1, p2, p3, s = evo.id(4) + local prefab = evo.builder() + :set(evo.pair(p1, s), 42) + :set(evo.pair(p2, s), 21) + :set(evo.pair(p3, s), 84) + :spawn() + local clone = evo.clone(prefab) + assert(evo.has(clone, evo.pair(p1, s)) and evo.get(clone, evo.pair(p1, s)) == 42) + assert(evo.has(clone, evo.pair(p2, s)) and evo.get(clone, evo.pair(p2, s)) == 21) + assert(evo.has(clone, evo.pair(p3, s)) and evo.get(clone, evo.pair(p3, s)) == 84) + end + + do + local p1, p2, p3, s = evo.id(4) + evo.set(p1, evo.UNIQUE) + evo.set(p2, evo.UNIQUE) + local prefab = evo.builder() + :set(evo.pair(p1, s), 42) + :set(evo.pair(p2, s), 21) + :set(evo.pair(p3, s), 84) + :spawn() + local clone = evo.clone(prefab) + assert(not evo.has(clone, evo.pair(p1, s)) and evo.get(clone, evo.pair(p1, s)) == nil) + assert(not evo.has(clone, evo.pair(p2, s)) and evo.get(clone, evo.pair(p2, s)) == nil) + assert(evo.has(clone, evo.pair(p3, s)) and evo.get(clone, evo.pair(p3, s)) == 84) + end +end + -- TODO -- builder:has/has_all/has_any should work with wildcards / remove too? -- should we provide wildcard support for get operations? diff --git a/evolved.lua b/evolved.lua index 8fa86dc..5d15fd7 100644 --- a/evolved.lua +++ b/evolved.lua @@ -378,17 +378,15 @@ end)() ---@param fmt string ---@param ... any ----@diagnostic disable-next-line: unused-local, unused-function local function __error_fmt(fmt, ...) - __lua_error(__lua_string_format('| evolved.lua | %s', + __lua_error(__lua_string_format('| evolved.lua (e) | %s', __lua_string_format(fmt, ...))) end ---@param fmt string ---@param ... any ----@diagnostic disable-next-line: unused-local, unused-function local function __warning_fmt(fmt, ...) - __lua_print(__lua_string_format('| evolved.lua | %s', + __lua_print(__lua_string_format('| evolved.lua (w) | %s', __lua_string_format(fmt, ...))) end @@ -4683,11 +4681,11 @@ end ---@nodiscard function __evolved_pack(index, version) if index < 1 or index > 2 ^ 20 - 1 then - __error_fmt('index (%d) is out of range [1, 2 ^ 20 - 1]', index) + __error_fmt('id index (%d) is out of range [1, 2 ^ 20 - 1]', index) end if version < 1 or version > 2 ^ 20 - 1 then - __error_fmt('version (%d) is out of range [1, 2 ^ 20 - 1]', version) + __error_fmt('id version (%d) is out of range [1, 2 ^ 20 - 1]', version) end return index @@ -4812,24 +4810,20 @@ end ---@return boolean ---@nodiscard function __evolved_alive(entity) - if not __evolved_is_pair(entity) then - local entity_index = entity % 2 ^ 20 + local entity_index, _, entity_options = __evolved_unpack(entity) + if entity_options < __PAIR_OPTS then if __freelist_ids[entity_index] ~= entity then return false end - - return true else - local primary_index = entity % 2 ^ 20 - - local primary = __freelist_ids[primary_index] --[[@as evolved.id?]] - if not primary or primary % 2 ^ 20 ~= primary_index then + local primary = __freelist_ids[entity_index] --[[@as evolved.id?]] + if not primary or primary % 2 ^ 20 ~= entity_index then return false end - - return true end + + return true end ---@param ... evolved.entity entities @@ -4878,24 +4872,20 @@ end ---@return boolean ---@nodiscard function __evolved_empty(entity) - if not __evolved_is_pair(entity) then - local entity_index = entity % 2 ^ 20 + local entity_index, _, entity_options = __evolved_unpack(entity) + if entity_options < __PAIR_OPTS then if __freelist_ids[entity_index] ~= entity then return true end - - return not __entity_chunks[entity_index] else - local primary_index = entity % 2 ^ 20 - - local primary = __freelist_ids[primary_index] --[[@as evolved.id?]] - if not primary or primary % 2 ^ 20 ~= primary_index then + local primary = __freelist_ids[entity_index] --[[@as evolved.id?]] + if not primary or primary % 2 ^ 20 ~= entity_index then return true end - - return not __entity_chunks[primary_index] end + + return not __entity_chunks[entity_index] end ---@param ... evolved.entity entities @@ -4945,36 +4935,22 @@ end ---@return boolean ---@nodiscard function __evolved_has(entity, fragment) - if not __evolved_is_pair(entity) then - local entity_index = entity % 2 ^ 20 + local entity_index, _, entity_options = __evolved_unpack(entity) + if entity_options < __PAIR_OPTS then if __freelist_ids[entity_index] ~= entity then return false end - - local entity_chunk = __entity_chunks[entity_index] - - if not entity_chunk then - return false - end - - return __chunk_has_fragment(entity_chunk, fragment) else - local primary_index = entity % 2 ^ 20 - - local primary = __freelist_ids[primary_index] --[[@as evolved.id?]] - if not primary or primary % 2 ^ 20 ~= primary_index then + local primary = __freelist_ids[entity_index] --[[@as evolved.id?]] + if not primary or primary % 2 ^ 20 ~= entity_index then return false end - - local primary_chunk = __entity_chunks[primary_index] - - if not primary_chunk then - return false - end - - return __chunk_has_fragment(primary_chunk, fragment) end + + local entity_chunk = __entity_chunks[entity_index] + + return entity_chunk and __chunk_has_fragment(entity_chunk, fragment) or false end ---@param entity evolved.entity @@ -4982,36 +4958,28 @@ end ---@return boolean ---@nodiscard function __evolved_has_all(entity, ...) - if not __evolved_is_pair(entity) then - local entity_index = entity % 2 ^ 20 + local argument_count = __lua_select('#', ...) - if __freelist_ids[entity_index] ~= entity then - return __lua_select('#', ...) == 0 - end - - local entity_chunk = __entity_chunks[entity_index] - - if not entity_chunk then - return __lua_select('#', ...) == 0 - end - - return __chunk_has_all_fragments(entity_chunk, ...) - else - local primary_index = entity % 2 ^ 20 - - local primary = __freelist_ids[primary_index] --[[@as evolved.id?]] - if not primary or primary % 2 ^ 20 ~= primary_index then - return __lua_select('#', ...) == 0 - end - - local primary_chunk = __entity_chunks[primary_index] - - if not primary_chunk then - return __lua_select('#', ...) == 0 - end - - return __chunk_has_all_fragments(primary_chunk, ...) + if argument_count == 0 then + return true end + + local entity_index, _, entity_options = __evolved_unpack(entity) + + if entity_options < __PAIR_OPTS then + if __freelist_ids[entity_index] ~= entity then + return false + end + else + local primary = __freelist_ids[entity_index] --[[@as evolved.id?]] + if not primary or primary % 2 ^ 20 ~= entity_index then + return false + end + end + + local entity_chunk = __entity_chunks[entity_index] + + return entity_chunk and __chunk_has_all_fragments(entity_chunk, ...) or false end ---@param entity evolved.entity @@ -5019,36 +4987,28 @@ end ---@return boolean ---@nodiscard function __evolved_has_any(entity, ...) - if not __evolved_is_pair(entity) then - local entity_index = entity % 2 ^ 20 + local argument_count = __lua_select('#', ...) + if argument_count == 0 then + return false + end + + local entity_index, _, entity_options = __evolved_unpack(entity) + + if entity_options < __PAIR_OPTS then if __freelist_ids[entity_index] ~= entity then return false end - - local entity_chunk = __entity_chunks[entity_index] - - if not entity_chunk then - return false - end - - return __chunk_has_any_fragments(entity_chunk, ...) else - local primary_index = entity % 2 ^ 20 - - local primary = __freelist_ids[primary_index] --[[@as evolved.id?]] - if not primary or primary % 2 ^ 20 ~= primary_index then + local primary = __freelist_ids[entity_index] --[[@as evolved.id?]] + if not primary or primary % 2 ^ 20 ~= entity_index then return false end - - local primary_chunk = __entity_chunks[primary_index] - - if not primary_chunk then - return false - end - - return __chunk_has_any_fragments(primary_chunk, ...) end + + local entity_chunk = __entity_chunks[entity_index] + + return entity_chunk and __chunk_has_any_fragments(entity_chunk, ...) or false end ---@param entity evolved.entity @@ -5056,59 +5016,66 @@ end ---@return evolved.component ... components ---@nodiscard function __evolved_get(entity, ...) - if not __evolved_is_pair(entity) then - local entity_index = entity % 2 ^ 20 + local entity_index, _, entity_options = __evolved_unpack(entity) + if entity_options < __PAIR_OPTS then if __freelist_ids[entity_index] ~= entity then return end - - local entity_chunk = __entity_chunks[entity_index] - - if not entity_chunk then - return - end - - local entity_place = __entity_places[entity_index] - return __chunk_get_components(entity_chunk, entity_place, ...) else - local primary_index = entity % 2 ^ 20 - - local primary = __freelist_ids[primary_index] --[[@as evolved.id?]] - if not primary or primary % 2 ^ 20 ~= primary_index then + local primary = __freelist_ids[entity_index] --[[@as evolved.id?]] + if not primary or primary % 2 ^ 20 ~= entity_index then return end - - local primary_chunk = __entity_chunks[primary_index] - - if not primary_chunk then - return - end - - local primary_place = __entity_places[primary_index] - return __chunk_get_components(primary_chunk, primary_place, ...) end + + local entity_chunk = __entity_chunks[entity_index] + + if not entity_chunk then + return + end + + local entity_place = __entity_places[entity_index] + return __chunk_get_components(entity_chunk, entity_place, ...) end ---@param entity evolved.entity ---@param fragment evolved.fragment ---@param component evolved.component function __evolved_set(entity, fragment, component) - if __debug_mode then - if __evolved_is_pair(entity) then - __error_fmt('the pair (%s) cannot be used as an entity', __id_name(entity)) + local entity_index, _, entity_options = __evolved_unpack(entity) + + if entity_options >= __PAIR_OPTS then + __error_fmt('the pair (%s) cannot be changed', __id_name(entity)) + elseif __freelist_ids[entity_index] ~= entity then + __error_fmt('the id (%s) is not alive and cannot be changed', __id_name(entity)) + end + + local fragment_primary, fragment_secondary, fragment_options = __evolved_unpack(fragment) + + if fragment_options < __PAIR_OPTS then + local fragment_index = fragment_primary + + if fragment_index == __ANY_INDEX then + __error_fmt('the id (%s) is a wildcard and cannot be set', __id_name(fragment)) + elseif __freelist_ids[fragment_index] ~= fragment then + __error_fmt('the id (%s) is not alive and cannot be set', __id_name(fragment)) + end + else + local primary_index, secondary_index = fragment_primary, fragment_secondary + + if fragment_options >= __PRI_WILDCARD_OPTS then + __error_fmt('the pair (%s) is a wildcard and cannot be set', __id_name(fragment)) end - if __evolved_is_wildcard(fragment) then - __error_fmt('the wildcard fragment (%s) cannot be used as a fragment', __id_name(fragment)) + local primary = __freelist_ids[primary_index] --[[@as evolved.id?]] + if not primary or primary % 2 ^ 20 ~= primary_index then + __error_fmt('the pair (%s) has no alive primary id and cannot be set', __id_name(fragment)) end - if not __evolved_alive(entity) then - __error_fmt('the entity (%s) is not alive and cannot be used', __id_name(entity)) - end - - if not __evolved_alive(fragment) then - __error_fmt('the fragment (%s) is not alive and cannot be used', __id_name(fragment)) + local secondary = __freelist_ids[secondary_index] --[[@as evolved.id?]] + if not secondary or secondary % 2 ^ 20 ~= secondary_index then + __error_fmt('the pair (%s) has no alive secondary id and cannot be set', __id_name(fragment)) end end @@ -5117,8 +5084,6 @@ function __evolved_set(entity, fragment, component) return end - local entity_index = entity % 2 ^ 20 - local entity_chunks = __entity_chunks local entity_places = __entity_places @@ -5348,15 +5313,12 @@ function __evolved_remove(entity, ...) return end - if __evolved_is_pair(entity) then - -- pairs cannot have fragments, nothing to remove - return - end + local entity_index, _, entity_options = __evolved_unpack(entity) - local entity_index = entity % 2 ^ 20 - - if __freelist_ids[entity_index] ~= entity then - -- this entity is not alive, nothing to remove + if entity_options >= __PAIR_OPTS then + __error_fmt('the pair (%s) cannot be changed', __id_name(entity)) + elseif __freelist_ids[entity_index] ~= entity then + -- the id is not alive, nothing to remove return end @@ -5468,12 +5430,12 @@ function __evolved_clear(...) for argument_index = 1, argument_count do ---@type evolved.entity local entity = __lua_select(argument_index, ...) - local entity_index = entity % 2 ^ 20 + local entity_index, _, entity_options = __evolved_unpack(entity) - if __evolved_is_pair(entity) then - -- pairs cannot have fragments, nothing to clear + if entity_options >= __PAIR_OPTS then + __warning_fmt('the pair (%s) cannot be changed', __id_name(entity)) elseif __freelist_ids[entity_index] ~= entity then - -- this entity is not alive, nothing to clear + -- the id is not alive, nothing to clear else local chunk = entity_chunks[entity_index] local place = entity_places[entity_index] @@ -5546,12 +5508,12 @@ function __evolved_destroy(...) for argument_index = 1, argument_count do ---@type evolved.entity local entity = __lua_select(argument_index, ...) - local entity_index = entity % 2 ^ 20 + local entity_index, _, entity_options = __evolved_unpack(entity) - if __evolved_is_pair(entity) then - -- pairs cannot be destroyed, nothing to do + if entity_options >= __PAIR_OPTS then + __warning_fmt('the pair (%s) cannot be changed', __id_name(entity)) elseif __freelist_ids[entity_index] ~= entity then - -- this entity is not alive, nothing to destroy + -- the id is not alive, nothing to destroy else local is_fragment = minor_chunks[entity] or @@ -5590,21 +5552,39 @@ end ---@param fragment evolved.fragment ---@param component evolved.component function __evolved_batch_set(query, fragment, component) - if __debug_mode then - if __evolved_is_pair(query) then - __error_fmt('the pair (%s) cannot be used as a query', __id_name(query)) + local query_index, _, query_options = __evolved_unpack(query) + + if query_options >= __PAIR_OPTS then + __error_fmt('the pair (%s) cannot be queried', __id_name(query)) + elseif __freelist_ids[query_index] ~= query then + __error_fmt('the id (%s) is not alive and cannot be queried', __id_name(query)) + end + + local fragment_primary, fragment_secondary, fragment_options = __evolved_unpack(fragment) + + if fragment_options < __PAIR_OPTS then + local fragment_index = fragment_primary + + if fragment_index == __ANY_INDEX then + __error_fmt('the id (%s) is a wildcard and cannot be set', __id_name(fragment)) + elseif __freelist_ids[fragment_index] ~= fragment then + __error_fmt('the id (%s) is not alive and cannot be set', __id_name(fragment)) + end + else + local primary_index, secondary_index = fragment_primary, fragment_secondary + + if fragment_options >= __PRI_WILDCARD_OPTS then + __error_fmt('the pair (%s) is a wildcard and cannot be set', __id_name(fragment)) end - if __evolved_is_wildcard(fragment) then - __error_fmt('the wildcard fragment (%s) cannot be used as a fragment', __id_name(fragment)) + local primary = __freelist_ids[primary_index] --[[@as evolved.id?]] + if not primary or primary % 2 ^ 20 ~= primary_index then + __error_fmt('the pair (%s) has no alive primary id and cannot be set', __id_name(fragment)) end - if not __evolved_alive(query) then - __error_fmt('the query (%s) is not alive and cannot be used', __id_name(query)) - end - - if not __evolved_alive(fragment) then - __error_fmt('the fragment (%s) is not alive and cannot be used', __id_name(fragment)) + local secondary = __freelist_ids[secondary_index] --[[@as evolved.id?]] + if not secondary or secondary % 2 ^ 20 ~= secondary_index then + __error_fmt('the pair (%s) has no alive secondary id and cannot be set', __id_name(fragment)) end end @@ -5645,6 +5625,14 @@ function __evolved_batch_remove(query, ...) return end + local query_index, _, query_options = __evolved_unpack(query) + + if query_options >= __PAIR_OPTS then + __error_fmt('the pair (%s) cannot be queried', __id_name(query)) + elseif __freelist_ids[query_index] ~= query then + __error_fmt('the id (%s) is not alive and cannot be queried', __id_name(query)) + end + if __defer_depth > 0 then __defer_batch_remove(query, ...) return @@ -5696,10 +5684,17 @@ function __evolved_batch_clear(...) for argument_index = 1, argument_count do ---@type evolved.query local query = __lua_select(argument_index, ...) + local query_index, _, query_options = __evolved_unpack(query) - for chunk in __evolved_execute(query) do - chunk_count = chunk_count + 1 - chunk_list[chunk_count] = chunk + if query_options >= __PAIR_OPTS then + __warning_fmt('the pair (%s) cannot be queried', __id_name(query)) + elseif __freelist_ids[query_index] ~= query then + __warning_fmt('the id (%s) is not alive and cannot be queried', __id_name(query)) + else + for chunk in __evolved_execute(query) do + chunk_count = chunk_count + 1 + chunk_list[chunk_count] = chunk + end end end @@ -5744,25 +5739,32 @@ function __evolved_batch_destroy(...) for argument_index = 1, argument_count do ---@type evolved.query local query = __lua_select(argument_index, ...) + local query_index, _, query_options = __evolved_unpack(query) - for chunk, entity_list, entity_count in __evolved_execute(query) do - clearing_chunk_count = clearing_chunk_count + 1 - clearing_chunk_list[clearing_chunk_count] = chunk + if query_options >= __PAIR_OPTS then + __warning_fmt('the pair (%s) cannot be queried', __id_name(query)) + elseif __freelist_ids[query_index] ~= query then + __warning_fmt('the id (%s) is not alive and cannot be queried', __id_name(query)) + else + for chunk, entity_list, entity_count in __evolved_execute(query) do + clearing_chunk_count = clearing_chunk_count + 1 + clearing_chunk_list[clearing_chunk_count] = chunk - for i = 1, entity_count do - local entity = entity_list[i] + for i = 1, entity_count do + local entity = entity_list[i] - local is_fragment = - minor_chunks[entity] or - minor_chunks[__primary_wildcard(entity)] or - minor_chunks[__secondary_wildcard(entity)] + local is_fragment = + minor_chunks[entity] or + minor_chunks[__primary_wildcard(entity)] or + minor_chunks[__secondary_wildcard(entity)] - if not is_fragment then - purging_entity_count = purging_entity_count + 1 - purging_entity_list[purging_entity_count] = entity - else - purging_fragment_count = purging_fragment_count + 1 - purging_fragment_list[purging_fragment_count] = entity + if not is_fragment then + purging_entity_count = purging_entity_count + 1 + purging_entity_list[purging_entity_count] = entity + else + purging_fragment_count = purging_fragment_count + 1 + purging_fragment_list[purging_fragment_count] = entity + end end end end @@ -5798,16 +5800,12 @@ end ---@return evolved.each_state? iterator_state ---@nodiscard function __evolved_each(entity) - if __evolved_is_pair(entity) then - -- pairs cannot be used as entities, nothing to iterate - return __iterator_fns.__each_iterator - end + local entity_index, _, entity_options = __evolved_unpack(entity) - local entity_index = entity % 2 ^ 20 - - if __freelist_ids[entity_index] ~= entity then - -- this entity is not alive, nothing to iterate - return __iterator_fns.__each_iterator + if entity_options >= __PAIR_OPTS then + __error_fmt('the pair (%s) cannot be iterated', __id_name(entity)) + elseif __freelist_ids[entity_index] ~= entity then + __error_fmt('the id (%s) is not alive and cannot be iterated', __id_name(entity)) end local entity_chunks = __entity_chunks @@ -5836,16 +5834,12 @@ end ---@return evolved.execute_state? iterator_state ---@nodiscard function __evolved_execute(query) - if __evolved_is_pair(query) then - -- pairs cannot be used as queries, nothing to execute - return __iterator_fns.__execute_major_iterator - end + local query_index, _, query_options = __evolved_unpack(query) - local query_index = query % 2 ^ 20 - - if __freelist_ids[query_index] ~= query then - -- this query is not alive, nothing to execute - return __iterator_fns.__execute_major_iterator + if query_options >= __PAIR_OPTS then + __error_fmt('the pair (%s) cannot be executed', __id_name(query)) + elseif __freelist_ids[query_index] ~= query then + __error_fmt('the id (%s) is not alive and cannot be executed', __id_name(query)) end ---@type evolved.chunk[] @@ -6035,13 +6029,14 @@ function __evolved_process(...) for argument_index = 1, argument_count do ---@type evolved.system local system = __lua_select(argument_index, ...) + local system_index, _, system_options = __evolved_unpack(system) - if __evolved_is_pair(system) then - -- pairs cannot be used as systems, nothing to process - elseif not __evolved_alive(system) then - -- this system is not alive, nothing to process + if system_options >= __PAIR_OPTS then + __warning_fmt('the pair (%s) cannot be processed', __id_name(system)) + elseif __freelist_ids[system_index] ~= system then + __warning_fmt('the id (%s) is not alive and cannot be processed', __id_name(system)) elseif __evolved_has(system, __DISABLED) then - -- this system is disabled, nothing to process + -- the system is disabled, nothing to process else __system_process(system) end From 22302cee75b8a6d4d1236f3be05359f6bdedd804 Mon Sep 17 00:00:00 2001 From: BlackMATov Date: Fri, 29 Aug 2025 18:19:19 +0700 Subject: [PATCH 06/12] primary/secondary iterators for pair entities --- develop/testing/pairs_tests.lua | 195 +++++++++++++++++++++ evolved.lua | 296 +++++++++++++++++++------------- 2 files changed, 369 insertions(+), 122 deletions(-) diff --git a/develop/testing/pairs_tests.lua b/develop/testing/pairs_tests.lua index bdfd3c7..d053922 100644 --- a/develop/testing/pairs_tests.lua +++ b/develop/testing/pairs_tests.lua @@ -1683,6 +1683,201 @@ do end end +do + ---@generic T1, T2 + ---@param first T1 + ---@param second T2 + ---@return T1 + ---@diagnostic disable-next-line: unused-local + local function fst(first, second) + return first + end + + ---@generic T1, T2 + ---@param first T1 + ---@param second T2 + ---@return T2 + ---@diagnostic disable-next-line: unused-local + local function snd(first, second) + return second + end + + do + local p, s1, s2, f = evo.id(4) + + local e = evo.builder() + :set(evo.pair(p, s1), 21) + :set(evo.pair(p, s2), 42) + :spawn() + + assert(fst(evo.primary(e, s1)) == p) + assert(snd(evo.primary(e, s1)) == 21) + assert(fst(evo.primary(e, s2)) == p) + assert(snd(evo.primary(e, s2)) == 42) + assert(fst(evo.primary(e, s1, 1)) == p) + assert(snd(evo.primary(e, s1, 1)) == 21) + assert(fst(evo.primary(e, s2, 1)) == p) + assert(snd(evo.primary(e, s2, 1)) == 42) + assert(fst(evo.primary(e, s1, 2)) == nil) + assert(snd(evo.primary(e, s1, 2)) == nil) + assert(fst(evo.primary(e, s2, 2)) == nil) + assert(snd(evo.primary(e, s2, 2)) == nil) + + assert(fst(evo.primary(evo.pair(e, f), s1)) == p) + assert(snd(evo.primary(evo.pair(e, f), s1)) == 21) + assert(fst(evo.primary(evo.pair(e, f), s2)) == p) + assert(snd(evo.primary(evo.pair(e, f), s2)) == 42) + assert(fst(evo.primary(evo.pair(e, f), s1, 1)) == p) + assert(snd(evo.primary(evo.pair(e, f), s1, 1)) == 21) + assert(fst(evo.primary(evo.pair(e, f), s2, 1)) == p) + assert(snd(evo.primary(evo.pair(e, f), s2, 1)) == 42) + assert(fst(evo.primary(evo.pair(e, f), s1, 2)) == nil) + assert(snd(evo.primary(evo.pair(e, f), s1, 2)) == nil) + assert(fst(evo.primary(evo.pair(e, f), s2, 2)) == nil) + assert(snd(evo.primary(evo.pair(e, f), s2, 2)) == nil) + + assert(fst(evo.secondary(e, p)) == s1) + assert(snd(evo.secondary(e, p)) == 21) + assert(fst(evo.secondary(e, p, 1)) == s1) + assert(snd(evo.secondary(e, p, 1)) == 21) + assert(fst(evo.secondary(e, p, 2)) == s2) + assert(snd(evo.secondary(e, p, 2)) == 42) + assert(fst(evo.secondary(e, p, 3)) == nil) + assert(snd(evo.secondary(e, p, 3)) == nil) + + assert(fst(evo.secondary(evo.pair(e, f), p)) == s1) + assert(snd(evo.secondary(evo.pair(e, f), p)) == 21) + assert(fst(evo.secondary(evo.pair(e, f), p, 1)) == s1) + assert(snd(evo.secondary(evo.pair(e, f), p, 1)) == 21) + assert(fst(evo.secondary(evo.pair(e, f), p, 2)) == s2) + assert(snd(evo.secondary(evo.pair(e, f), p, 2)) == 42) + assert(fst(evo.secondary(evo.pair(e, f), p, 3)) == nil) + assert(snd(evo.secondary(evo.pair(e, f), p, 3)) == nil) + + assert(fst(evo.primary(evo.pair(f, e), s1)) == nil) + assert(snd(evo.primary(evo.pair(f, e), s1)) == nil) + assert(fst(evo.primary(evo.pair(f, e), s2)) == nil) + assert(snd(evo.primary(evo.pair(f, e), s2)) == nil) + assert(fst(evo.secondary(evo.pair(f, e), p)) == nil) + assert(snd(evo.secondary(evo.pair(f, e), p)) == nil) + end + + do + local p, s1, s2, f = evo.id(4) + + local e = evo.builder() + :set(evo.pair(p, s1), 21) + :set(evo.pair(p, s2), 42) + :spawn() + + assert(evo.primary_count(e, p) == 0) + assert(evo.primary_count(e, s1) == 1) + assert(evo.primary_count(e, s2) == 1) + assert(evo.secondary_count(e, p) == 2) + assert(evo.secondary_count(e, s1) == 0) + assert(evo.secondary_count(e, s2) == 0) + + assert(evo.primary_count(evo.pair(e, f), p) == 0) + assert(evo.primary_count(evo.pair(e, f), s1) == 1) + assert(evo.primary_count(evo.pair(e, f), s2) == 1) + assert(evo.secondary_count(evo.pair(e, f), p) == 2) + assert(evo.secondary_count(evo.pair(e, f), s1) == 0) + assert(evo.secondary_count(evo.pair(e, f), s2) == 0) + + assert(evo.primary_count(evo.pair(f, e), p) == 0) + assert(evo.primary_count(evo.pair(f, e), s1) == 0) + assert(evo.primary_count(evo.pair(f, e), s2) == 0) + assert(evo.secondary_count(evo.pair(f, e), p) == 0) + assert(evo.secondary_count(evo.pair(f, e), s1) == 0) + assert(evo.secondary_count(evo.pair(f, e), s2) == 0) + end + + do + local p, s1, s2, f = evo.id(4) + + local e = evo.builder() + :set(evo.pair(p, s1), 21) + :set(evo.pair(p, s2), 42) + :spawn() + + do + local iter, state = evo.primaries(e, p) + local fragment, component = iter(state) + assert(fragment == nil and component == nil) + + + iter, state = evo.primaries(evo.pair(e, f), p) + fragment, component = iter(state) + assert(fragment == nil and component == nil) + end + + do + local iter, state = evo.primaries(e, s1) + local fragment, component = iter(state) + assert(fragment == p and component == 21) + fragment, component = iter(state) + assert(fragment == nil and component == nil) + + iter, state = evo.primaries(evo.pair(e, f), s1) + fragment, component = iter(state) + assert(fragment == p and component == 21) + fragment, component = iter(state) + assert(fragment == nil and component == nil) + end + + do + local iter, state = evo.primaries(e, s2) + local fragment, component = iter(state) + assert(fragment == p and component == 42) + fragment, component = iter(state) + assert(fragment == nil and component == nil) + + iter, state = evo.primaries(evo.pair(e, f), s2) + fragment, component = iter(state) + assert(fragment == p and component == 42) + fragment, component = iter(state) + assert(fragment == nil and component == nil) + end + end + + do + local p, s1, s2, f = evo.id(4) + + local e = evo.builder() + :set(evo.pair(p, s1), 21) + :set(evo.pair(p, s2), 42) + :spawn() + + do + local iter, state = evo.secondaries(e, s1) + local fragment, component = iter(state) + assert(fragment == nil and component == nil) + + iter, state = evo.secondaries(evo.pair(e, f), s1) + fragment, component = iter(state) + assert(fragment == nil and component == nil) + end + + do + local iter, state = evo.secondaries(e, p) + local fragment, component = iter(state) + assert(fragment == s1 and component == 21) + fragment, component = iter(state) + assert(fragment == s2 and component == 42) + fragment, component = iter(state) + assert(fragment == nil and component == nil) + + iter, state = evo.secondaries(evo.pair(e, f), p) + fragment, component = iter(state) + assert(fragment == s1 and component == 21) + fragment, component = iter(state) + assert(fragment == s2 and component == 42) + fragment, component = iter(state) + assert(fragment == nil and component == nil) + end + end +end + -- TODO -- builder:has/has_all/has_any should work with wildcards / remove too? -- should we provide wildcard support for get operations? diff --git a/evolved.lua b/evolved.lua index 5d15fd7..fd8cf30 100644 --- a/evolved.lua +++ b/evolved.lua @@ -4681,15 +4681,14 @@ end ---@nodiscard function __evolved_pack(index, version) if index < 1 or index > 2 ^ 20 - 1 then - __error_fmt('id index (%d) is out of range [1, 2 ^ 20 - 1]', index) + __error_fmt('the index (%d) is out of range [1, 2 ^ 20 - 1]', index) end if version < 1 or version > 2 ^ 20 - 1 then - __error_fmt('id version (%d) is out of range [1, 2 ^ 20 - 1]', version) + __error_fmt('the version (%d) is out of range [1, 2 ^ 20 - 1]', version) end - return index - + version * 2 ^ 20 --[[@as evolved.id]] + return index + version * 2 ^ 20 --[[@as evolved.id]] end ---@param id evolved.id @@ -4817,8 +4816,8 @@ function __evolved_alive(entity) return false end else - local primary = __freelist_ids[entity_index] --[[@as evolved.id?]] - if not primary or primary % 2 ^ 20 ~= entity_index then + local entity_primary_id = __freelist_ids[entity_index] --[[@as evolved.id?]] + if not entity_primary_id or entity_primary_id % 2 ^ 20 ~= entity_index then return false end end @@ -4879,8 +4878,8 @@ function __evolved_empty(entity) return true end else - local primary = __freelist_ids[entity_index] --[[@as evolved.id?]] - if not primary or primary % 2 ^ 20 ~= entity_index then + local entity_primary_id = __freelist_ids[entity_index] --[[@as evolved.id?]] + if not entity_primary_id or entity_primary_id % 2 ^ 20 ~= entity_index then return true end end @@ -4942,8 +4941,8 @@ function __evolved_has(entity, fragment) return false end else - local primary = __freelist_ids[entity_index] --[[@as evolved.id?]] - if not primary or primary % 2 ^ 20 ~= entity_index then + local entity_primary_id = __freelist_ids[entity_index] --[[@as evolved.id?]] + if not entity_primary_id or entity_primary_id % 2 ^ 20 ~= entity_index then return false end end @@ -4971,8 +4970,8 @@ function __evolved_has_all(entity, ...) return false end else - local primary = __freelist_ids[entity_index] --[[@as evolved.id?]] - if not primary or primary % 2 ^ 20 ~= entity_index then + local entity_primary_id = __freelist_ids[entity_index] --[[@as evolved.id?]] + if not entity_primary_id or entity_primary_id % 2 ^ 20 ~= entity_index then return false end end @@ -5000,8 +4999,8 @@ function __evolved_has_any(entity, ...) return false end else - local primary = __freelist_ids[entity_index] --[[@as evolved.id?]] - if not primary or primary % 2 ^ 20 ~= entity_index then + local entity_primary_id = __freelist_ids[entity_index] --[[@as evolved.id?]] + if not entity_primary_id or entity_primary_id % 2 ^ 20 ~= entity_index then return false end end @@ -5023,8 +5022,8 @@ function __evolved_get(entity, ...) return end else - local primary = __freelist_ids[entity_index] --[[@as evolved.id?]] - if not primary or primary % 2 ^ 20 ~= entity_index then + local entity_primary_id = __freelist_ids[entity_index] --[[@as evolved.id?]] + if not entity_primary_id or entity_primary_id % 2 ^ 20 ~= entity_index then return end end @@ -5068,13 +5067,13 @@ function __evolved_set(entity, fragment, component) __error_fmt('the pair (%s) is a wildcard and cannot be set', __id_name(fragment)) end - local primary = __freelist_ids[primary_index] --[[@as evolved.id?]] - if not primary or primary % 2 ^ 20 ~= primary_index then + local fragment_primary_id = __freelist_ids[primary_index] --[[@as evolved.id?]] + if not fragment_primary_id or fragment_primary_id % 2 ^ 20 ~= primary_index then __error_fmt('the pair (%s) has no alive primary id and cannot be set', __id_name(fragment)) end - local secondary = __freelist_ids[secondary_index] --[[@as evolved.id?]] - if not secondary or secondary % 2 ^ 20 ~= secondary_index then + local fragment_secondary_id = __freelist_ids[secondary_index] --[[@as evolved.id?]] + if not fragment_secondary_id or fragment_secondary_id % 2 ^ 20 ~= secondary_index then __error_fmt('the pair (%s) has no alive secondary id and cannot be set', __id_name(fragment)) end end @@ -5577,13 +5576,13 @@ function __evolved_batch_set(query, fragment, component) __error_fmt('the pair (%s) is a wildcard and cannot be set', __id_name(fragment)) end - local primary = __freelist_ids[primary_index] --[[@as evolved.id?]] - if not primary or primary % 2 ^ 20 ~= primary_index then + local fragment_primary_id = __freelist_ids[primary_index] --[[@as evolved.id?]] + if not fragment_primary_id or fragment_primary_id % 2 ^ 20 ~= primary_index then __error_fmt('the pair (%s) has no alive primary id and cannot be set', __id_name(fragment)) end - local secondary = __freelist_ids[secondary_index] --[[@as evolved.id?]] - if not secondary or secondary % 2 ^ 20 ~= secondary_index then + local fragment_secondary_id = __freelist_ids[secondary_index] --[[@as evolved.id?]] + if not fragment_secondary_id or fragment_secondary_id % 2 ^ 20 ~= secondary_index then __error_fmt('the pair (%s) has no alive secondary id and cannot be set', __id_name(fragment)) end end @@ -6121,29 +6120,27 @@ end function __evolved_pair(primary, secondary) local primary_index, _, primary_options = __evolved_unpack(primary) if primary_options >= __PAIR_OPTS then - __error_fmt('the primary id (%s) is a pair and cannot be used as a primary id of a pair', + __error_fmt('the primary id (%s) is a pair and cannot be used as a primary id of a new pair', __id_name(primary)) end local secondary_index, _, secondary_options = __evolved_unpack(secondary) if secondary_options >= __PAIR_OPTS then - __error_fmt('the secondary id (%s) is a pair and cannot be used as a secondary id of a pair', + __error_fmt('the secondary id (%s) is a pair and cannot be used as a secondary id of a new pair', __id_name(secondary)) end local pair_options = __PAIR_OPTS - if primary == __ANY and secondary == __ANY then + if primary_index == __ANY_INDEX and secondary_index == __ANY_INDEX then pair_options = __ANY_WILDCARD_OPTS - elseif primary == __ANY then + elseif primary_index == __ANY_INDEX then pair_options = __PRI_WILDCARD_OPTS - elseif secondary == __ANY then + elseif secondary_index == __ANY_INDEX then pair_options = __SEC_WILDCARD_OPTS end - return primary_index - + secondary_index * 2 ^ 20 - + pair_options * 2 ^ 40 --[[@as evolved.pair]] + return primary_index + secondary_index * 2 ^ 20 + pair_options * 2 ^ 40 --[[@as evolved.pair]] end ---@param pair evolved.pair @@ -6151,44 +6148,41 @@ end ---@return evolved.id secondary ---@nodiscard function __evolved_unpair(pair) - local pair_primary, pair_secondary, pair_options = __evolved_unpack(pair) + local primary_index, secondary_index, pair_options = __evolved_unpack(pair) if pair_options < __PAIR_OPTS then - __error_fmt('the id (%s) is not a pair and cannot be unpaired', - __id_name(pair)) + __error_fmt('the id (%s) is not a pair and cannot be unpaired', __id_name(pair)) end - local primary = __freelist_ids[pair_primary] --[[@as evolved.id?]] - if not primary or primary % 2 ^ 20 ~= pair_primary then - __error_fmt('the primary id of the pair (%s) is not alive and cannot be unpaired', - __id_name(pair)) + local pair_primary_id = __freelist_ids[primary_index] --[[@as evolved.id?]] + if not pair_primary_id or pair_primary_id % 2 ^ 20 ~= primary_index then + __error_fmt('the pair (%s) has not alive primary id and cannot be unpaired', __id_name(pair)) else - ---@cast primary -? + ---@cast pair_primary_id -? end - local secondary = __freelist_ids[pair_secondary] --[[@as evolved.id?]] - if not secondary or secondary % 2 ^ 20 ~= pair_secondary then - __error_fmt('the secondary id of the pair (%s) is not alive and cannot be unpaired', - __id_name(pair)) + local pair_secondary_id = __freelist_ids[secondary_index] --[[@as evolved.id?]] + if not pair_secondary_id or pair_secondary_id % 2 ^ 20 ~= secondary_index then + __error_fmt('the pair (%s) has not alive secondary id and cannot be unpaired', __id_name(pair)) else - ---@cast secondary -? + ---@cast pair_secondary_id -? end - return primary, secondary + return pair_primary_id, pair_secondary_id end ---@param id evolved.id ---@return boolean ---@nodiscard function __evolved_is_pair(id) - return id % 2 ^ 41 >= 2 ^ 40 + return id >= __PAIR_OPTS * 2 ^ 40 end ---@param id evolved.id ---@return boolean ---@nodiscard function __evolved_is_wildcard(id) - return id % 2 ^ 43 >= 2 ^ 41 + return id >= __PRI_WILDCARD_OPTS * 2 ^ 40 end ---@param entity evolved.entity @@ -6200,22 +6194,32 @@ end function __evolved_primary(entity, secondary, index) index = index or 1 - if __evolved_is_pair(entity) then - -- pairs are always empty + local entity_index, _, entity_options = __evolved_unpack(entity) + + if entity_options < __PAIR_OPTS then + if __freelist_ids[entity_index] ~= entity then + return + end + else + local entity_primary_id = __freelist_ids[entity_index] --[[@as evolved.id?]] + if not entity_primary_id or entity_primary_id % 2 ^ 20 ~= entity_index then + return + end + end + + local entity_chunk = __entity_chunks[entity_index] + + if not entity_chunk then return end - local entity_index = entity % 2 ^ 20 + local secondary_index, _, secondary_options = __evolved_unpack(secondary) - if __freelist_ids[entity_index] ~= entity then - -- non-alive entities do not have any fragments - return + if secondary_options >= __PAIR_OPTS then + __error_fmt('the pair (%s) cannot be used as a secondary fragment', __id_name(secondary)) end - local chunk = __entity_chunks[entity_index] - local place = __entity_places[entity_index] - - local secondary_fragments = chunk and chunk.__secondary_pairs[secondary % 2 ^ 20] + local secondary_fragments = entity_chunk.__secondary_pairs[secondary_index] local secondary_fragment_list = secondary_fragments and secondary_fragments.__item_list local secondary_fragment_count = secondary_fragments and secondary_fragments.__item_count or 0 @@ -6226,10 +6230,11 @@ function __evolved_primary(entity, secondary, index) local secondary_fragment = secondary_fragment_list[index] local primary, _ = __evolved_unpair(secondary_fragment) - local component_index = chunk.__component_indices[secondary_fragment] - local component_storage = chunk.__component_storages[component_index] + local component_index = entity_chunk.__component_indices[secondary_fragment] + local component_storage = entity_chunk.__component_storages[component_index] - return primary, component_storage and component_storage[place] + local entity_place = __entity_places[entity_index] + return primary, component_storage and component_storage[entity_place] end ---@param entity evolved.entity @@ -6241,22 +6246,32 @@ end function __evolved_secondary(entity, primary, index) index = index or 1 - if __evolved_is_pair(entity) then - -- pairs are always empty + local entity_index, _, entity_options = __evolved_unpack(entity) + + if entity_options < __PAIR_OPTS then + if __freelist_ids[entity_index] ~= entity then + return + end + else + local entity_primary_id = __freelist_ids[entity_index] --[[@as evolved.id?]] + if not entity_primary_id or entity_primary_id % 2 ^ 20 ~= entity_index then + return + end + end + + local entity_chunk = __entity_chunks[entity_index] + + if not entity_chunk then return end - local entity_index = entity % 2 ^ 20 + local primary_index, _, primary_options = __evolved_unpack(primary) - if __freelist_ids[entity_index] ~= entity then - -- non-alive entities do not have any fragments - return + if primary_options >= __PAIR_OPTS then + __error_fmt('the pair (%s) cannot be used as a primary fragment', __id_name(primary)) end - local chunk = __entity_chunks[entity_index] - local place = __entity_places[entity_index] - - local primary_fragments = chunk and chunk.__primary_pairs[primary % 2 ^ 20] + local primary_fragments = entity_chunk.__primary_pairs[primary_index] local primary_fragment_list = primary_fragments and primary_fragments.__item_list local primary_fragment_count = primary_fragments and primary_fragments.__item_count or 0 @@ -6267,10 +6282,11 @@ function __evolved_secondary(entity, primary, index) local primary_fragment = primary_fragment_list[index] local _, secondary = __evolved_unpair(primary_fragment) - local component_index = chunk.__component_indices[primary_fragment] - local component_storage = chunk.__component_storages[component_index] + local component_index = entity_chunk.__component_indices[primary_fragment] + local component_storage = entity_chunk.__component_storages[component_index] - return secondary, component_storage and component_storage[place] + local entity_place = __entity_places[entity_index] + return secondary, component_storage and component_storage[entity_place] end ---@param entity evolved.entity @@ -6279,26 +6295,34 @@ end ---@return evolved.primaries_state? iterator_state ---@nodiscard function __evolved_primaries(entity, secondary) - if __evolved_is_pair(entity) then - -- pairs are always empty + local entity_index, _, entity_options = __evolved_unpack(entity) + + if entity_options < __PAIR_OPTS then + if __freelist_ids[entity_index] ~= entity then + return __iterator_fns.__primaries_iterator + end + else + local entity_primary_id = __freelist_ids[entity_index] --[[@as evolved.id?]] + if not entity_primary_id or entity_primary_id % 2 ^ 20 ~= entity_index then + return __iterator_fns.__primaries_iterator + end + end + + local entity_chunk = __entity_chunks[entity_index] + + if not entity_chunk then return __iterator_fns.__primaries_iterator end - local entity_index = entity % 2 ^ 20 + local secondary_index, _, secondary_options = __evolved_unpack(secondary) - if __freelist_ids[entity_index] ~= entity then - -- non-alive entities do not have any fragments - return __iterator_fns.__primaries_iterator + if secondary_options >= __PAIR_OPTS then + __error_fmt('the pair (%s) cannot be used as a secondary fragment', __id_name(secondary)) end - local chunk = __entity_chunks[entity_index] - local place = __entity_places[entity_index] - - local secondary_index = secondary % 2 ^ 20 - local secondary_fragments = chunk and chunk.__secondary_pairs[secondary_index] + local secondary_fragments = entity_chunk.__secondary_pairs[secondary_index] if not secondary_fragments or secondary_fragments.__item_count == 0 then - -- no primaries for this secondary return __iterator_fns.__primaries_iterator end @@ -6306,8 +6330,8 @@ function __evolved_primaries(entity, secondary) local primaries_state = __acquire_table(__table_pool_tag.primaries_state) primaries_state[1] = __structural_changes - primaries_state[2] = chunk - primaries_state[3] = place + primaries_state[2] = entity_chunk + primaries_state[3] = __entity_places[entity_index] primaries_state[4] = secondary_index primaries_state[5] = 1 @@ -6320,26 +6344,34 @@ end ---@return evolved.secondaries_state? iterator_state ---@nodiscard function __evolved_secondaries(entity, primary) - if __evolved_is_pair(entity) then - -- pairs are always empty + local entity_index, _, entity_options = __evolved_unpack(entity) + + if entity_options < __PAIR_OPTS then + if __freelist_ids[entity_index] ~= entity then + return __iterator_fns.__secondaries_iterator + end + else + local entity_primary_id = __freelist_ids[entity_index] --[[@as evolved.id?]] + if not entity_primary_id or entity_primary_id % 2 ^ 20 ~= entity_index then + return __iterator_fns.__secondaries_iterator + end + end + + local entity_chunk = __entity_chunks[entity_index] + + if not entity_chunk then return __iterator_fns.__secondaries_iterator end - local entity_index = entity % 2 ^ 20 + local primary_index, _, primary_options = __evolved_unpack(primary) - if __freelist_ids[entity_index] ~= entity then - -- non-alive entities do not have any fragments - return __iterator_fns.__secondaries_iterator + if primary_options >= __PAIR_OPTS then + __error_fmt('the pair (%s) cannot be used as a primary fragment', __id_name(primary)) end - local chunk = __entity_chunks[entity_index] - local place = __entity_places[entity_index] - - local primary_index = primary % 2 ^ 20 - local primary_fragments = chunk and chunk.__primary_pairs[primary_index] + local primary_fragments = entity_chunk.__primary_pairs[primary_index] if not primary_fragments or primary_fragments.__item_count == 0 then - -- no secondaries for this primary return __iterator_fns.__secondaries_iterator end @@ -6347,8 +6379,8 @@ function __evolved_secondaries(entity, primary) local secondaries_state = __acquire_table(__table_pool_tag.secondaries_state) secondaries_state[1] = __structural_changes - secondaries_state[2] = chunk - secondaries_state[3] = place + secondaries_state[2] = entity_chunk + secondaries_state[3] = __entity_places[entity_index] secondaries_state[4] = primary_index secondaries_state[5] = 1 @@ -6360,22 +6392,32 @@ end ---@return integer ---@nodiscard function __evolved_primary_count(entity, secondary) - if __evolved_is_pair(entity) then - -- pairs are always empty + local entity_index, _, entity_options = __evolved_unpack(entity) + + if entity_options < __PAIR_OPTS then + if __freelist_ids[entity_index] ~= entity then + return 0 + end + else + local entity_primary_id = __freelist_ids[entity_index] --[[@as evolved.id?]] + if not entity_primary_id or entity_primary_id % 2 ^ 20 ~= entity_index then + return 0 + end + end + + local entity_chunk = __entity_chunks[entity_index] + + if not entity_chunk then return 0 end - local entity_index = entity % 2 ^ 20 + local secondary_index, _, secondary_options = __evolved_unpack(secondary) - if __freelist_ids[entity_index] ~= entity then - -- non-alive entities do not have any fragments - return 0 + if secondary_options >= __PAIR_OPTS then + __error_fmt('the pair (%s) cannot be used as a secondary fragment', __id_name(secondary)) end - local chunk = __entity_chunks[entity_index] - - local secondary_index = secondary % 2 ^ 20 - local secondary_fragments = chunk and chunk.__secondary_pairs[secondary_index] + local secondary_fragments = entity_chunk.__secondary_pairs[secondary_index] return secondary_fragments and secondary_fragments.__item_count or 0 end @@ -6385,22 +6427,32 @@ end ---@return integer ---@nodiscard function __evolved_secondary_count(entity, primary) - if __evolved_is_pair(entity) then - -- pairs are always empty + local entity_index, _, entity_options = __evolved_unpack(entity) + + if entity_options < __PAIR_OPTS then + if __freelist_ids[entity_index] ~= entity then + return 0 + end + else + local entity_primary_id = __freelist_ids[entity_index] --[[@as evolved.id?]] + if not entity_primary_id or entity_primary_id % 2 ^ 20 ~= entity_index then + return 0 + end + end + + local entity_chunk = __entity_chunks[entity_index] + + if not entity_chunk then return 0 end - local entity_index = entity % 2 ^ 20 + local primary_index, _, primary_options = __evolved_unpack(primary) - if __freelist_ids[entity_index] ~= entity then - -- non-alive entities do not have any fragments - return 0 + if primary_options >= __PAIR_OPTS then + __error_fmt('the pair (%s) cannot be used as a primary fragment', __id_name(primary)) end - local chunk = __entity_chunks[entity_index] - - local primary_index = primary % 2 ^ 20 - local primary_fragments = chunk and chunk.__primary_pairs[primary_index] + local primary_fragments = entity_chunk.__primary_pairs[primary_index] return primary_fragments and primary_fragments.__item_count or 0 end From 4cd83935466f5e952aef1da1de31498b307ca60a Mon Sep 17 00:00:00 2001 From: BlackMATov Date: Mon, 1 Sep 2025 00:33:10 +0700 Subject: [PATCH 07/12] public api works only with non-pair ids in has/get functions --- develop/testing/pairs_tests.lua | 122 ++++-- evolved.lua | 708 ++++++++++++++++++++------------ 2 files changed, 545 insertions(+), 285 deletions(-) diff --git a/develop/testing/pairs_tests.lua b/develop/testing/pairs_tests.lua index d053922..932c0ec 100644 --- a/develop/testing/pairs_tests.lua +++ b/develop/testing/pairs_tests.lua @@ -91,31 +91,31 @@ do assert(evo.has_any(e12, evo.pair(p1, evo.ANY), evo.pair(p2, evo.ANY))) end --- do --- local p1, s1, p2, s2 = evo.id(4) --- evo.set(p1, s1) --- evo.set(s1, p1) --- evo.set(p2, s2) --- assert(not evo.empty(evo.pair(p1, s1))) --- assert(not evo.empty(evo.pair(p2, s2))) --- assert(not evo.empty_all(evo.pair(p1, s1), evo.pair(p2, s2))) --- assert(not evo.empty_any(evo.pair(p1, s1), evo.pair(p2, s2))) --- assert(not evo.empty_all(evo.pair(p1, s1), evo.pair(p2, s2), p1)) --- assert(not evo.empty_any(evo.pair(p1, s1), evo.pair(p2, s2), p1)) --- assert(not evo.empty_all(evo.pair(p1, s1), evo.pair(p2, s2), s2)) --- assert(evo.empty_any(evo.pair(p1, s1), evo.pair(p2, s2), s2)) --- end +do + local p1, s1, p2, s2 = evo.id(4) + evo.set(p1, s1) + evo.set(s1, p1) + evo.set(p2, s2) + assert(evo.empty(evo.pair(p1, s1))) + assert(evo.empty(evo.pair(p2, s2))) + assert(evo.empty_all(evo.pair(p1, s1), evo.pair(p2, s2))) + assert(evo.empty_any(evo.pair(p1, s1), evo.pair(p2, s2))) + assert(not evo.empty_all(evo.pair(p1, s1), evo.pair(p2, s2), p1)) + assert(evo.empty_any(evo.pair(p1, s1), evo.pair(p2, s2), p1)) + assert(evo.empty_all(evo.pair(p1, s1), evo.pair(p2, s2), s2)) + assert(evo.empty_any(evo.pair(p1, s1), evo.pair(p2, s2), s2)) +end do local p1, s1 = evo.id(2) evo.set(p1, s1) evo.set(s1, p1) assert(not evo.has(evo.pair(p1, s1), p1)) - assert(evo.has(evo.pair(p1, s1), s1)) + assert(not evo.has(evo.pair(p1, s1), s1)) assert(not evo.has_all(evo.pair(p1, s1), p1, s1)) - assert(evo.has_any(evo.pair(p1, s1), p1, s1)) + assert(not evo.has_any(evo.pair(p1, s1), p1, s1)) assert(evo.get(evo.pair(p1, s1), p1) == nil) - assert(evo.get(evo.pair(p1, s1), s1) == true) + assert(evo.get(evo.pair(p1, s1), s1) == nil) end do @@ -1117,7 +1117,7 @@ do assert(evo.empty(evo.pair(p, s))) evo.set(p, f) - assert(not evo.empty(evo.pair(p, s))) + assert(evo.empty(evo.pair(p, s))) evo.destroy(p) assert(evo.empty(evo.pair(p, s))) @@ -1128,10 +1128,10 @@ do assert(evo.empty(evo.pair(p, s))) evo.set(p, f) - assert(not evo.empty(evo.pair(p, s))) + assert(evo.empty(evo.pair(p, s))) evo.destroy(s) - assert(not evo.empty(evo.pair(p, s))) + assert(evo.empty(evo.pair(p, s))) evo.destroy(p) assert(evo.empty(evo.pair(p, s))) @@ -1144,10 +1144,10 @@ do evo.set(p, f, 42) assert(evo.has(p, f)) - assert(evo.has(evo.pair(p, s), f)) + assert(not evo.has(evo.pair(p, s), f)) assert(not evo.has(evo.pair(s, p), f)) assert(evo.get(p, f) == 42) - assert(evo.get(evo.pair(p, s), f) == 42) + assert(evo.get(evo.pair(p, s), f) == nil) assert(evo.get(evo.pair(s, p), f) == nil) end end @@ -1643,7 +1643,7 @@ do do local p, s = evo.id(2) evo.destroy(s) - assert(evo.alive(evo.pair(p, s))) + assert(not evo.alive(evo.pair(p, s))) end do @@ -1876,6 +1876,82 @@ do assert(fragment == nil and component == nil) end end + + do + local p, s1, s2 = evo.id(3) + + local e = evo.builder() + :set(evo.pair(p, s1), 21) + :set(evo.pair(p, s2), 42) + :spawn() + + do + local iter, state = evo.each(e) + local fragment, component = iter(state) + assert(fragment == evo.pair(p, s1) and component == 21) + fragment, component = iter(state) + assert(fragment == evo.pair(p, s2) and component == 42) + fragment, component = iter(state) + assert(fragment == nil and component == nil) + end + end +end + +do + do + local p, s1, s2 = evo.id(3) + + local e = evo.spawn { + [evo.pair(p, s1)] = 21, + [evo.pair(p, s2)] = 42, + } + + assert(evo.has(e, evo.pair(p, s1)) and evo.get(e, evo.pair(p, s1)) == 21) + assert(evo.has(e, evo.pair(p, s2)) and evo.get(e, evo.pair(p, s2)) == 42) + + evo.destroy(p) + + assert(not evo.has(e, evo.pair(p, s1)) and evo.get(e, evo.pair(p, s1)) == nil) + assert(not evo.has(e, evo.pair(p, s2)) and evo.get(e, evo.pair(p, s2)) == nil) + end + + do + local p, s1, s2 = evo.id(3) + + local e = evo.spawn { + [evo.pair(p, s1)] = 21, + [evo.pair(p, s2)] = 42, + } + + assert(evo.has(e, evo.pair(p, s1)) and evo.get(e, evo.pair(p, s1)) == 21) + assert(evo.has(e, evo.pair(p, s2)) and evo.get(e, evo.pair(p, s2)) == 42) + + evo.destroy(s1) + + assert(not evo.has(e, evo.pair(p, s1)) and evo.get(e, evo.pair(p, s1)) == nil) + assert(evo.has(e, evo.pair(p, s2)) and evo.get(e, evo.pair(p, s2)) == 42) + + evo.destroy(s2) + + assert(not evo.has(e, evo.pair(p, s1)) and evo.get(e, evo.pair(p, s1)) == nil) + assert(not evo.has(e, evo.pair(p, s2)) and evo.get(e, evo.pair(p, s2)) == nil) + end + + do + local p, s = evo.id(2) + + evo.destroy(s) + + evo.debug_mode(false) + + local e = evo.spawn { + [evo.pair(p, s)] = 21, + } + + evo.debug_mode(true) + + assert(evo.has(e, evo.pair(p, s)) and evo.get(e, evo.pair(p, s)) == 21) + end end -- TODO diff --git a/evolved.lua b/evolved.lua index fd8cf30..1e3e867 100644 --- a/evolved.lua +++ b/evolved.lua @@ -935,46 +935,149 @@ local __evolved_builder --- --- -local __id_name +local __primary_has +local __primary_has_all +local __primary_has_any +local __primary_get local __primary_wildcard local __secondary_wildcard +local __universal_name local __component_storage ----@param id evolved.id ----@return string +local __chunk_with_fragment +local __chunk_with_components +local __chunk_without_fragment +local __chunk_without_fragments +local __chunk_without_unique_fragments + +local __chunk_fragments +local __chunk_components + +local __chunk_has_fragment +local __chunk_has_all_fragments +local __chunk_has_all_fragment_list +local __chunk_has_any_fragments +local __chunk_has_any_fragment_list +local __chunk_get_components + +local __chunk_required_fragments +local __fragment_required_fragments + +---@param id evolved.id | evolved.pair +---@param fragment evolved.fragment +---@return boolean ---@nodiscard -function __id_name(id) - local id_primary, id_secondary, id_options = __evolved_unpack(id) +function __primary_has(id, fragment) + local id_index, _, id_options = __evolved_unpack(id) if id_options < __PAIR_OPTS then - ---@type string? - local id_name = __evolved_get(id, __NAME) - - if id_name then - return id_name + if __freelist_ids[id_index] ~= id then + return false end else - ---@type string?, string? - local pair_primary_id_name, pair_secondary_id_name - - local pair_primary_id = __freelist_ids[id_primary] --[[@as evolved.id?]] - if pair_primary_id and pair_primary_id % 2 ^ 20 == id_primary then - pair_primary_id_name = __id_name(pair_primary_id) - end - - local pair_secondary_id = __freelist_ids[id_secondary] --[[@as evolved.id?]] - if pair_secondary_id and pair_secondary_id % 2 ^ 20 == id_secondary then - pair_secondary_id_name = __id_name(pair_secondary_id) - end - - if pair_primary_id_name and pair_secondary_id_name then - return __lua_string_format('${%s,%s}', pair_primary_id_name, pair_secondary_id_name) + local id_primary_id = __freelist_ids[id_index] --[[@as evolved.id?]] + if not id_primary_id or id_primary_id % 2 ^ 20 ~= id_index then + return false end end - return __lua_string_format('$%d#%d:%d:%d', id, id_primary, id_secondary, id_options) + local id_chunk = __entity_chunks[id_index] + + return id_chunk and __chunk_has_fragment(id_chunk, fragment) or false +end + +---@param id evolved.id | evolved.pair +---@param ... evolved.fragment fragments +---@return boolean +---@nodiscard +function __primary_has_all(id, ...) + local argument_count = select('#', ...) + + if argument_count == 0 then + return true + end + + local id_index, _, id_options = __evolved_unpack(id) + + if id_options < __PAIR_OPTS then + if __freelist_ids[id_index] ~= id then + return false + end + else + local id_primary_id = __freelist_ids[id_index] --[[@as evolved.id?]] + if not id_primary_id or id_primary_id % 2 ^ 20 ~= id_index then + return false + end + end + + local id_chunk = __entity_chunks[id_index] + + return id_chunk and __chunk_has_all_fragments(id_chunk, ...) or false +end + +---@param id evolved.id | evolved.pair +---@param ... evolved.fragment fragments +---@return boolean +---@nodiscard +function __primary_has_any(id, ...) + local argument_count = select('#', ...) + + if argument_count == 0 then + return false + end + + local id_index, _, id_options = __evolved_unpack(id) + + if id_options < __PAIR_OPTS then + if __freelist_ids[id_index] ~= id then + return false + end + else + local id_primary_id = __freelist_ids[id_index] --[[@as evolved.id?]] + if not id_primary_id or id_primary_id % 2 ^ 20 ~= id_index then + return false + end + end + + local id_chunk = __entity_chunks[id_index] + + return id_chunk and __chunk_has_any_fragments(id_chunk, ...) or false +end + +---@param id evolved.id | evolved.pair +---@param ... evolved.fragment fragments +---@return evolved.component ... components +---@nodiscard +function __primary_get(id, ...) + local fragment_count = select('#', ...) + + if fragment_count == 0 then + return + end + + local id_index, _, id_options = __evolved_unpack(id) + + if id_options < __PAIR_OPTS then + if __freelist_ids[id_index] ~= id then + return + end + else + local id_primary_id = __freelist_ids[id_index] --[[@as evolved.id?]] + if not id_primary_id or id_primary_id % 2 ^ 20 ~= id_index then + return + end + end + + local id_chunk = __entity_chunks[id_index] + + if not id_chunk then + return + end + + local id_place = __entity_places[id_index] + return __chunk_get_components(id_chunk, id_place, ...) end ---@param secondary evolved.id | integer id or index @@ -1009,6 +1112,47 @@ function __secondary_wildcard(primary) + __SEC_WILDCARD_OPTS * 2 ^ 40 --[[@as evolved.pair]] end +--- +--- +--- +--- +--- + +---@param id evolved.id +---@return string +---@nodiscard +function __universal_name(id) + local id_primary, id_secondary, id_options = __evolved_unpack(id) + + if id_options < __PAIR_OPTS then + ---@type string? + local id_name = __evolved_get(id, __NAME) + + if id_name then + return id_name + end + else + ---@type string?, string? + local pair_primary_id_name, pair_secondary_id_name + + local pair_primary_id = __freelist_ids[id_primary] --[[@as evolved.id?]] + if pair_primary_id and pair_primary_id % 2 ^ 20 == id_primary then + pair_primary_id_name = __universal_name(pair_primary_id) + end + + local pair_secondary_id = __freelist_ids[id_secondary] --[[@as evolved.id?]] + if pair_secondary_id and pair_secondary_id % 2 ^ 20 == id_secondary then + pair_secondary_id_name = __universal_name(pair_secondary_id) + end + + if pair_primary_id_name and pair_secondary_id_name then + return __lua_string_format('${%s,%s}', pair_primary_id_name, pair_secondary_id_name) + end + end + + return __lua_string_format('$%d#%d:%d:%d', id, id_primary, id_secondary, id_options) +end + ---@param fragment evolved.fragment ---@return evolved.storage ---@nodiscard @@ -1229,7 +1373,7 @@ local __update_major_chunks_trace ---@return evolved.chunk ---@nodiscard function __new_chunk(chunk_parent, chunk_fragment) - if __evolved_is_wildcard(chunk_fragment) then + if chunk_fragment >= __PRI_WILDCARD_OPTS * 2 ^ 40 then __error_fmt('chunk cannot contain wildcard fragments') end @@ -1279,7 +1423,7 @@ function __new_chunk(chunk_parent, chunk_fragment) chunk_fragment_list[chunk_fragment_count] = chunk_fragment end - if __evolved_is_pair(chunk_fragment) then + if chunk_fragment >= __PAIR_OPTS * 2 ^ 40 then chunk_pair_count = chunk_pair_count + 1 chunk_pair_list[chunk_pair_count] = chunk_fragment @@ -1392,7 +1536,7 @@ function __new_chunk(chunk_parent, chunk_fragment) __assoc_list_insert(minor_chunks, chunk) end - if __evolved_is_pair(chunk_fragment) then + if chunk_fragment >= __PAIR_OPTS * 2 ^ 40 then local major = chunk_fragment local major_primary_index, major_secondary_index = __evolved_unpack(major) @@ -1502,7 +1646,7 @@ function __update_chunk_tags(chunk) local fragment = fragment_list[i] local component_index = component_indices[fragment] - if component_index and __evolved_has(fragment, __TAG) then + if component_index and __primary_has(fragment, __TAG) then if component_index ~= component_count then local last_component_storage = component_storages[component_count] local last_component_fragment = component_fragments[component_count] @@ -1519,7 +1663,7 @@ function __update_chunk_tags(chunk) chunk.__component_count = component_count end - if not component_index and not __evolved_has(fragment, __TAG) then + if not component_index and not __primary_has(fragment, __TAG) then component_count = component_count + 1 chunk.__component_count = component_count @@ -1532,7 +1676,7 @@ function __update_chunk_tags(chunk) ---@type evolved.default?, evolved.duplicate? local fragment_default, fragment_duplicate = - __evolved_get(fragment, __DEFAULT, __DUPLICATE) + __primary_get(fragment, __DEFAULT, __DUPLICATE) if fragment_duplicate then for place = 1, chunk.__entity_count do @@ -1558,35 +1702,35 @@ function __update_chunk_flags(chunk) local chunk_fragment = chunk.__fragment local has_setup_hooks = (chunk_parent ~= nil and chunk_parent.__has_setup_hooks) - or __evolved_has_any(chunk_fragment, __DEFAULT, __DUPLICATE) + or __primary_has_any(chunk_fragment, __DEFAULT, __DUPLICATE) local has_assign_hooks = (chunk_parent ~= nil and chunk_parent.__has_assign_hooks) - or __evolved_has_any(chunk_fragment, __ON_SET, __ON_ASSIGN) + or __primary_has_any(chunk_fragment, __ON_SET, __ON_ASSIGN) local has_insert_hooks = (chunk_parent ~= nil and chunk_parent.__has_insert_hooks) - or __evolved_has_any(chunk_fragment, __ON_SET, __ON_INSERT) + or __primary_has_any(chunk_fragment, __ON_SET, __ON_INSERT) local has_remove_hooks = (chunk_parent ~= nil and chunk_parent.__has_remove_hooks) - or __evolved_has(chunk_fragment, __ON_REMOVE) + or __primary_has(chunk_fragment, __ON_REMOVE) - local has_pair_major = __evolved_is_pair(chunk_fragment) + local has_pair_major = chunk_fragment >= __PAIR_OPTS * 2 ^ 40 local has_pair_minors = chunk_parent ~= nil and chunk_parent.__has_pair_fragments local has_pair_fragments = has_pair_major or has_pair_minors - local has_unique_major = __evolved_has(chunk_fragment, __UNIQUE) + local has_unique_major = __primary_has(chunk_fragment, __UNIQUE) local has_unique_minors = chunk_parent ~= nil and chunk_parent.__has_unique_fragments local has_unique_fragments = has_unique_major or has_unique_minors - local has_explicit_major = __evolved_has(chunk_fragment, __EXPLICIT) + local has_explicit_major = __primary_has(chunk_fragment, __EXPLICIT) local has_explicit_minors = chunk_parent ~= nil and chunk_parent.__has_explicit_fragments local has_explicit_fragments = has_explicit_major or has_explicit_minors - local has_internal_major = __evolved_has(chunk_fragment, __INTERNAL) + local has_internal_major = __primary_has(chunk_fragment, __INTERNAL) local has_internal_minors = chunk_parent ~= nil and chunk_parent.__has_internal_fragments local has_internal_fragments = has_internal_major or has_internal_minors local has_required_fragments = (chunk_parent ~= nil and chunk_parent.__has_required_fragments) - or __evolved_has(chunk_fragment, __REQUIRES) + or __primary_has(chunk_fragment, __REQUIRES) chunk.__has_setup_hooks = has_setup_hooks chunk.__has_assign_hooks = has_assign_hooks @@ -1616,7 +1760,7 @@ end ---@param trace fun(chunk: evolved.chunk, ...: any) ---@param ... any additional trace arguments function __trace_major_chunks(major, trace, ...) - if __evolved_is_pair(major) then + if major >= __PAIR_OPTS * 2 ^ 40 then __error_fmt('trace operations on pair fragments are not supported') end @@ -1693,7 +1837,7 @@ end ---@param trace fun(chunk: evolved.chunk, ...: any) ---@param ... any additional trace arguments function __trace_minor_chunks(minor, trace, ...) - if __evolved_is_pair(minor) then + if minor >= __PAIR_OPTS * 2 ^ 40 then __error_fmt('trace operations on pair fragments are not supported') end @@ -1776,7 +1920,7 @@ end ---@param fragment evolved.fragment ---@return evolved.chunk ---@nodiscard -local function __chunk_with_fragment(chunk, fragment) +function __chunk_with_fragment(chunk, fragment) if not chunk then local root_chunk = __root_chunks[fragment] return root_chunk or __new_chunk(nil, fragment) @@ -1823,7 +1967,7 @@ end ---@param components table ---@return evolved.chunk? ---@nodiscard -local function __chunk_with_components(chunk, components) +function __chunk_with_components(chunk, components) for fragment in __lua_next, components do chunk = __chunk_with_fragment(chunk, fragment) end @@ -1835,7 +1979,7 @@ end ---@param fragment evolved.fragment ---@return evolved.chunk? ---@nodiscard -local function __chunk_without_fragment(chunk, fragment) +function __chunk_without_fragment(chunk, fragment) if not chunk then return nil end @@ -1849,11 +1993,11 @@ local function __chunk_without_fragment(chunk, fragment) if without_fragment_edge then return without_fragment_edge end end - if chunk.__has_pair_fragments and __evolved_is_wildcard(fragment) then - local primary_index, secondary_index, fragment_opts = + if chunk.__has_pair_fragments and fragment >= __PRI_WILDCARD_OPTS * 2 ^ 40 then + local fragment_primary, fragment_secondary, fragment_options = __evolved_unpack(fragment) - if fragment_opts == __ANY_WILDCARD_OPTS then + if fragment_options == __ANY_WILDCARD_OPTS then while chunk and chunk.__has_pair_major do chunk = chunk.__parent end @@ -1874,15 +2018,15 @@ local function __chunk_without_fragment(chunk, fragment) end return sib_chunk - elseif fragment_opts == __PRI_WILDCARD_OPTS then - if not chunk.__secondary_pairs[secondary_index] then + elseif fragment_options == __PRI_WILDCARD_OPTS then + if not chunk.__secondary_pairs[fragment_secondary] then -- the chunk does not have such pairs return chunk end local sib_chunk = chunk.__parent - while sib_chunk and sib_chunk.__has_pair_fragments and sib_chunk.__secondary_pairs[secondary_index] do + while sib_chunk and sib_chunk.__has_pair_fragments and sib_chunk.__secondary_pairs[fragment_secondary] do sib_chunk = sib_chunk.__parent end @@ -1894,7 +2038,7 @@ local function __chunk_without_fragment(chunk, fragment) for ini_pair_index = lst_pair_index, ini_pair_count do local ini_pair = ini_pair_list[ini_pair_index] local _, ini_secondary_index = __evolved_unpack(ini_pair) - if ini_secondary_index ~= secondary_index then + if ini_secondary_index ~= fragment_secondary then sib_chunk = __chunk_with_fragment(sib_chunk, ini_pair) end end @@ -1905,15 +2049,15 @@ local function __chunk_without_fragment(chunk, fragment) end return sib_chunk - elseif fragment_opts == __SEC_WILDCARD_OPTS then - if not chunk.__primary_pairs[primary_index] then + elseif fragment_options == __SEC_WILDCARD_OPTS then + if not chunk.__primary_pairs[fragment_primary] then -- the chunk does not have such pairs return chunk end local sib_chunk = chunk.__parent - while sib_chunk and sib_chunk.__has_pair_fragments and sib_chunk.__primary_pairs[primary_index] do + while sib_chunk and sib_chunk.__has_pair_fragments and sib_chunk.__primary_pairs[fragment_primary] do sib_chunk = sib_chunk.__parent end @@ -1925,7 +2069,7 @@ local function __chunk_without_fragment(chunk, fragment) for ini_pair_index = lst_pair_index, ini_pair_count do local ini_pair = ini_pair_list[ini_pair_index] local ini_primary_index, _ = __evolved_unpack(ini_pair) - if ini_primary_index ~= primary_index then + if ini_primary_index ~= fragment_primary then sib_chunk = __chunk_with_fragment(sib_chunk, ini_pair) end end @@ -1973,7 +2117,7 @@ end ---@param ... evolved.fragment fragments ---@return evolved.chunk? ---@nodiscard -local function __chunk_without_fragments(chunk, ...) +function __chunk_without_fragments(chunk, ...) if not chunk then return nil end @@ -1996,7 +2140,7 @@ end ---@param chunk? evolved.chunk ---@return evolved.chunk? ---@nodiscard -local function __chunk_without_unique_fragments(chunk) +function __chunk_without_unique_fragments(chunk) while chunk and chunk.__has_unique_major do chunk = chunk.__parent end @@ -2018,7 +2162,7 @@ local function __chunk_without_unique_fragments(chunk) for ini_fragment_index = lst_fragment_index, ini_fragment_count do local ini_fragment = ini_fragment_list[ini_fragment_index] - if not __evolved_has(ini_fragment, __UNIQUE) then + if not __primary_has(ini_fragment, __UNIQUE) then sib_chunk = __chunk_with_fragment(sib_chunk, ini_fragment) end end @@ -2036,7 +2180,7 @@ end ---@param ... evolved.fragment tail_fragments ---@return evolved.chunk ---@nodiscard -local function __chunk_fragments(head_fragment, ...) +function __chunk_fragments(head_fragment, ...) local chunk = __root_chunks[head_fragment] or __new_chunk(nil, head_fragment) @@ -2053,7 +2197,7 @@ end ---@param components table ---@return evolved.chunk? ---@nodiscard -local function __chunk_components(components) +function __chunk_components(components) local head_fragment = __lua_next(components) if not head_fragment then @@ -2081,22 +2225,22 @@ end ---@param fragment evolved.fragment ---@return boolean ---@nodiscard -local function __chunk_has_fragment(chunk, fragment) +function __chunk_has_fragment(chunk, fragment) if chunk.__fragment_set[fragment] then return true end - if chunk.__has_pair_fragments and __evolved_is_wildcard(fragment) then - local primary_index, secondary_index, fragment_opts = + if chunk.__has_pair_fragments and fragment >= __PRI_WILDCARD_OPTS * 2 ^ 40 then + local fragment_primary, fragment_secondary, fragment_options = __evolved_unpack(fragment) - if fragment_opts == __ANY_WILDCARD_OPTS then + if fragment_options == __ANY_WILDCARD_OPTS then return true - elseif fragment_opts == __PRI_WILDCARD_OPTS then - local secondary_fragments = chunk.__secondary_pairs[secondary_index] + elseif fragment_options == __PRI_WILDCARD_OPTS then + local secondary_fragments = chunk.__secondary_pairs[fragment_secondary] return secondary_fragments and secondary_fragments.__item_count > 0 - elseif fragment_opts == __SEC_WILDCARD_OPTS then - local primary_fragments = chunk.__primary_pairs[primary_index] + elseif fragment_options == __SEC_WILDCARD_OPTS then + local primary_fragments = chunk.__primary_pairs[fragment_primary] return primary_fragments and primary_fragments.__item_count > 0 end end @@ -2108,7 +2252,7 @@ end ---@param ... evolved.fragment fragments ---@return boolean ---@nodiscard -local function __chunk_has_all_fragments(chunk, ...) +function __chunk_has_all_fragments(chunk, ...) local fragment_count = __lua_select('#', ...) if fragment_count == 0 then @@ -2160,7 +2304,7 @@ end ---@param fragment_count integer ---@return boolean ---@nodiscard -local function __chunk_has_all_fragment_list(chunk, fragment_list, fragment_count) +function __chunk_has_all_fragment_list(chunk, fragment_list, fragment_count) if fragment_count == 0 then return true end @@ -2193,7 +2337,7 @@ end ---@param ... evolved.fragment fragments ---@return boolean ---@nodiscard -local function __chunk_has_any_fragments(chunk, ...) +function __chunk_has_any_fragments(chunk, ...) local fragment_count = __lua_select('#', ...) if fragment_count == 0 then @@ -2245,7 +2389,7 @@ end ---@param fragment_count integer ---@return boolean ---@nodiscard -local function __chunk_has_any_fragment_list(chunk, fragment_list, fragment_count) +function __chunk_has_any_fragment_list(chunk, fragment_list, fragment_count) if fragment_count == 0 then return false end @@ -2279,7 +2423,7 @@ end ---@param ... evolved.fragment fragments ---@return evolved.component ... components ---@nodiscard -local function __chunk_get_components(chunk, place, ...) +function __chunk_get_components(chunk, place, ...) local fragment_count = __lua_select('#', ...) if fragment_count == 0 then @@ -2347,7 +2491,7 @@ end ---@param req_fragment_count integer ---@return integer ---@nodiscard -local function __chunk_required_fragments(chunk, req_fragment_set, req_fragment_list, req_fragment_count) +function __chunk_required_fragments(chunk, req_fragment_set, req_fragment_list, req_fragment_count) ---@type evolved.fragment[] local fragment_stack = __acquire_table(__table_pool_tag.fragment_list) local fragment_stack_size = 0 @@ -2369,7 +2513,7 @@ local function __chunk_required_fragments(chunk, req_fragment_set, req_fragment_ fragment_stack[fragment_stack_size] = nil fragment_stack_size = fragment_stack_size - 1 - if __evolved_is_pair(stack_fragment) then + if stack_fragment >= __PAIR_OPTS * 2 ^ 40 then stack_fragment = __evolved_unpair(stack_fragment) end @@ -2403,7 +2547,7 @@ end ---@param req_fragment_count integer ---@return integer ---@nodiscard -local function __fragment_required_fragments(fragment, req_fragment_set, req_fragment_list, req_fragment_count) +function __fragment_required_fragments(fragment, req_fragment_set, req_fragment_list, req_fragment_count) ---@type evolved.fragment[] local fragment_stack = __acquire_table(__table_pool_tag.fragment_list) local fragment_stack_size = 0 @@ -2419,7 +2563,7 @@ local function __fragment_required_fragments(fragment, req_fragment_set, req_fra fragment_stack[fragment_stack_size] = nil fragment_stack_size = fragment_stack_size - 1 - if __evolved_is_pair(stack_fragment) then + if stack_fragment >= __PAIR_OPTS * 2 ^ 40 then stack_fragment = __evolved_unpair(stack_fragment) end @@ -2588,7 +2732,7 @@ local function __spawn_entity(entity, components) if component_index then ---@type evolved.duplicate? local fragment_duplicate = - __evolved_get(fragment, __DUPLICATE) + __primary_get(fragment, __DUPLICATE) local new_component = component @@ -2617,7 +2761,7 @@ local function __spawn_entity(entity, components) if req_component_index then ---@type evolved.default?, evolved.duplicate? local req_fragment_default, req_fragment_duplicate = - __evolved_get(req_fragment, __DEFAULT, __DUPLICATE) + __primary_get(req_fragment, __DEFAULT, __DUPLICATE) local req_component = req_fragment_default @@ -2676,7 +2820,7 @@ local function __spawn_entity(entity, components) ---@type evolved.set_hook?, evolved.insert_hook? local fragment_on_set, fragment_on_insert = - __evolved_get(fragment, __ON_SET, __ON_INSERT) + __primary_get(fragment, __ON_SET, __ON_INSERT) local component_index = chunk_component_indices[fragment] @@ -2789,7 +2933,7 @@ local function __clone_entity(entity, prefab, components) if component_index then ---@type evolved.duplicate? local fragment_duplicate = - __evolved_get(fragment, __DUPLICATE) + __primary_get(fragment, __DUPLICATE) local prefab_component_storage = prefab_component_storages[prefab_component_index] local prefab_component = prefab_component_storage[prefab_place] @@ -2839,7 +2983,7 @@ local function __clone_entity(entity, prefab, components) if component_index then ---@type evolved.duplicate? local fragment_duplicate = - __evolved_get(fragment, __DUPLICATE) + __primary_get(fragment, __DUPLICATE) local new_component = component @@ -2868,7 +3012,7 @@ local function __clone_entity(entity, prefab, components) if req_component_index then ---@type evolved.default?, evolved.duplicate? local req_fragment_default, req_fragment_duplicate = - __evolved_get(req_fragment, __DEFAULT, __DUPLICATE) + __primary_get(req_fragment, __DEFAULT, __DUPLICATE) local req_component = req_fragment_default @@ -2927,7 +3071,7 @@ local function __clone_entity(entity, prefab, components) ---@type evolved.set_hook?, evolved.insert_hook? local fragment_on_set, fragment_on_insert = - __evolved_get(fragment, __ON_SET, __ON_INSERT) + __primary_get(fragment, __ON_SET, __ON_INSERT) local component_index = chunk_component_indices[fragment] @@ -3137,7 +3281,7 @@ local function __destroy_entity_list(entity_list, entity_count) local fragment = chunk_fragment_list[chunk_fragment_index] ---@type evolved.remove_hook? - local fragment_on_remove = __evolved_get(fragment, __ON_REMOVE) + local fragment_on_remove = __primary_get(fragment, __ON_REMOVE) if fragment_on_remove then local component_index = chunk_component_indices[fragment] @@ -3213,7 +3357,7 @@ local function __destroy_fragment_list(fragment_list, fragment_count) releasing_fragment_count = releasing_fragment_count + 1 releasing_fragment_list[releasing_fragment_count] = processing_fragment - local processing_fragment_destruction_policy = __evolved_get(processing_fragment, __DESTRUCTION_POLICY) + local processing_fragment_destruction_policy = __primary_get(processing_fragment, __DESTRUCTION_POLICY) or __DESTRUCTION_POLICY_REMOVE_FRAGMENT if processing_fragment_destruction_policy == __DESTRUCTION_POLICY_DESTROY_ENTITY then @@ -3235,7 +3379,7 @@ local function __destroy_fragment_list(fragment_list, fragment_count) remove_fragment_policy_fragment_list[remove_fragment_policy_fragment_count] = processing_fragment else __error_fmt('unknown DESTRUCTION_POLICY (%s) on (%s)', - __id_name(processing_fragment_destruction_policy), __id_name(processing_fragment)) + __universal_name(processing_fragment_destruction_policy), __universal_name(processing_fragment)) end end end @@ -3319,7 +3463,7 @@ function __chunk_set(old_chunk, fragment, component) if old_chunk_has_setup_hooks or old_chunk_has_assign_hooks then fragment_default, fragment_duplicate, fragment_on_set, fragment_on_assign = - __evolved_get(fragment, __DEFAULT, __DUPLICATE, __ON_SET, __ON_ASSIGN) + __primary_get(fragment, __DEFAULT, __DUPLICATE, __ON_SET, __ON_ASSIGN) end if fragment_on_set or fragment_on_assign then @@ -3445,7 +3589,7 @@ function __chunk_set(old_chunk, fragment, component) if new_chunk_has_setup_hooks or new_chunk_has_insert_hooks then fragment_default, fragment_duplicate, fragment_on_set, fragment_on_insert = - __evolved_get(fragment, __DEFAULT, __DUPLICATE, __ON_SET, __ON_INSERT) + __primary_get(fragment, __DEFAULT, __DUPLICATE, __ON_SET, __ON_INSERT) end if new_entity_count == 0 then @@ -3588,7 +3732,7 @@ function __chunk_set(old_chunk, fragment, component) if new_chunk_has_setup_hooks or new_chunk_has_insert_hooks then req_fragment_default, req_fragment_duplicate, req_fragment_on_set, req_fragment_on_insert = - __evolved_get(req_fragment, __DEFAULT, __DUPLICATE, __ON_SET, __ON_INSERT) + __primary_get(req_fragment, __DEFAULT, __DUPLICATE, __ON_SET, __ON_INSERT) end if req_fragment_on_set or req_fragment_on_insert then @@ -3725,7 +3869,7 @@ function __chunk_remove(old_chunk, ...) if not new_fragment_set[fragment] then ---@type evolved.remove_hook? - local fragment_on_remove = __evolved_get(fragment, __ON_REMOVE) + local fragment_on_remove = __primary_get(fragment, __ON_REMOVE) if fragment_on_remove then local old_component_index = old_component_indices[fragment] @@ -3841,7 +3985,7 @@ function __chunk_clear(chunk) local fragment = chunk_fragment_list[chunk_fragment_index] ---@type evolved.remove_hook? - local fragment_on_remove = __evolved_get(fragment, __ON_REMOVE) + local fragment_on_remove = __primary_get(fragment, __ON_REMOVE) if fragment_on_remove then local component_index = chunk_component_indices[fragment] @@ -4650,27 +4794,27 @@ function __evolved_name(...) if id_count == 1 then local id1 = ... - return __id_name(id1) + return __universal_name(id1) end if id_count == 2 then local id1, id2 = ... - return __id_name(id1), __id_name(id2) + return __universal_name(id1), __universal_name(id2) end if id_count == 3 then local id1, id2, id3 = ... - return __id_name(id1), __id_name(id2), __id_name(id3) + return __universal_name(id1), __universal_name(id2), __universal_name(id3) end if id_count == 4 then local id1, id2, id3, id4 = ... - return __id_name(id1), __id_name(id2), __id_name(id3), __id_name(id4) + return __universal_name(id1), __universal_name(id2), __universal_name(id3), __universal_name(id4) end do local id1, id2, id3, id4 = ... - return __id_name(id1), __id_name(id2), __id_name(id3), __id_name(id4), + return __universal_name(id1), __universal_name(id2), __universal_name(id3), __universal_name(id4), __evolved_name(__lua_select(5, ...)) end end @@ -4750,7 +4894,8 @@ function __evolved_spawn(components) if __debug_mode then for fragment in __lua_next, components do if not __evolved_alive(fragment) then - __error_fmt('the fragment (%s) is not alive and cannot be used', __id_name(fragment)) + __error_fmt('the fragment (%s) is not alive and cannot be used', + __universal_name(fragment)) end end end @@ -4780,12 +4925,14 @@ function __evolved_clone(prefab, components) if __debug_mode then if not __evolved_alive(prefab) then - __error_fmt('the prefab (%s) is not alive and cannot be used', __id_name(prefab)) + __error_fmt('the prefab (%s) is not alive and cannot be used', + __universal_name(prefab)) end for fragment in __lua_next, components do if not __evolved_alive(fragment) then - __error_fmt('the fragment (%s) is not alive and cannot be used', __id_name(fragment)) + __error_fmt('the fragment (%s) is not alive and cannot be used', + __universal_name(fragment)) end end end @@ -4809,15 +4956,20 @@ end ---@return boolean ---@nodiscard function __evolved_alive(entity) - local entity_index, _, entity_options = __evolved_unpack(entity) + local entity_primary, entity_secondary, entity_options = __evolved_unpack(entity) if entity_options < __PAIR_OPTS then - if __freelist_ids[entity_index] ~= entity then + if __freelist_ids[entity_primary] ~= entity then return false end else - local entity_primary_id = __freelist_ids[entity_index] --[[@as evolved.id?]] - if not entity_primary_id or entity_primary_id % 2 ^ 20 ~= entity_index then + local entity_primary_id = __freelist_ids[entity_primary] --[[@as evolved.id?]] + if not entity_primary_id or entity_primary_id % 2 ^ 20 ~= entity_primary then + return false + end + + local entity_secondary_id = __freelist_ids[entity_secondary] --[[@as evolved.id?]] + if not entity_secondary_id or entity_secondary_id % 2 ^ 20 ~= entity_secondary then return false end end @@ -4871,20 +5023,17 @@ end ---@return boolean ---@nodiscard function __evolved_empty(entity) - local entity_index, _, entity_options = __evolved_unpack(entity) + local entity_primary, _, entity_options = __evolved_unpack(entity) - if entity_options < __PAIR_OPTS then - if __freelist_ids[entity_index] ~= entity then - return true - end - else - local entity_primary_id = __freelist_ids[entity_index] --[[@as evolved.id?]] - if not entity_primary_id or entity_primary_id % 2 ^ 20 ~= entity_index then - return true - end + if entity_options >= __PAIR_OPTS then + return true end - return not __entity_chunks[entity_index] + if __freelist_ids[entity_primary] ~= entity then + return true + end + + return not __entity_chunks[entity_primary] end ---@param ... evolved.entity entities @@ -4936,15 +5085,12 @@ end function __evolved_has(entity, fragment) local entity_index, _, entity_options = __evolved_unpack(entity) - if entity_options < __PAIR_OPTS then - if __freelist_ids[entity_index] ~= entity then - return false - end - else - local entity_primary_id = __freelist_ids[entity_index] --[[@as evolved.id?]] - if not entity_primary_id or entity_primary_id % 2 ^ 20 ~= entity_index then - return false - end + if entity_options >= __PAIR_OPTS then + return false + end + + if __freelist_ids[entity_index] ~= entity then + return false end local entity_chunk = __entity_chunks[entity_index] @@ -4965,15 +5111,12 @@ function __evolved_has_all(entity, ...) local entity_index, _, entity_options = __evolved_unpack(entity) - if entity_options < __PAIR_OPTS then - if __freelist_ids[entity_index] ~= entity then - return false - end - else - local entity_primary_id = __freelist_ids[entity_index] --[[@as evolved.id?]] - if not entity_primary_id or entity_primary_id % 2 ^ 20 ~= entity_index then - return false - end + if entity_options >= __PAIR_OPTS then + return false + end + + if __freelist_ids[entity_index] ~= entity then + return false end local entity_chunk = __entity_chunks[entity_index] @@ -4994,15 +5137,12 @@ function __evolved_has_any(entity, ...) local entity_index, _, entity_options = __evolved_unpack(entity) - if entity_options < __PAIR_OPTS then - if __freelist_ids[entity_index] ~= entity then - return false - end - else - local entity_primary_id = __freelist_ids[entity_index] --[[@as evolved.id?]] - if not entity_primary_id or entity_primary_id % 2 ^ 20 ~= entity_index then - return false - end + if entity_options >= __PAIR_OPTS then + return false + end + + if __freelist_ids[entity_index] ~= entity then + return false end local entity_chunk = __entity_chunks[entity_index] @@ -5017,15 +5157,12 @@ end function __evolved_get(entity, ...) local entity_index, _, entity_options = __evolved_unpack(entity) - if entity_options < __PAIR_OPTS then - if __freelist_ids[entity_index] ~= entity then - return - end - else - local entity_primary_id = __freelist_ids[entity_index] --[[@as evolved.id?]] - if not entity_primary_id or entity_primary_id % 2 ^ 20 ~= entity_index then - return - end + if entity_options >= __PAIR_OPTS then + return + end + + if __freelist_ids[entity_index] ~= entity then + return end local entity_chunk = __entity_chunks[entity_index] @@ -5045,36 +5182,40 @@ function __evolved_set(entity, fragment, component) local entity_index, _, entity_options = __evolved_unpack(entity) if entity_options >= __PAIR_OPTS then - __error_fmt('the pair (%s) cannot be changed', __id_name(entity)) + __error_fmt('the pair (%s) cannot be changed', + __universal_name(entity)) elseif __freelist_ids[entity_index] ~= entity then - __error_fmt('the id (%s) is not alive and cannot be changed', __id_name(entity)) + __error_fmt('the id (%s) is not alive and cannot be changed', + __universal_name(entity)) end - local fragment_primary, fragment_secondary, fragment_options = __evolved_unpack(fragment) + local fragment_primary, fragment_secondary, fragment_options = + __evolved_unpack(fragment) if fragment_options < __PAIR_OPTS then - local fragment_index = fragment_primary - - if fragment_index == __ANY_INDEX then - __error_fmt('the id (%s) is a wildcard and cannot be set', __id_name(fragment)) - elseif __freelist_ids[fragment_index] ~= fragment then - __error_fmt('the id (%s) is not alive and cannot be set', __id_name(fragment)) + if fragment_primary == __ANY_INDEX then + __error_fmt('the id (%s) is a wildcard and cannot be set', + __universal_name(fragment)) + elseif __freelist_ids[fragment_primary] ~= fragment then + __error_fmt('the id (%s) is not alive and cannot be set', + __universal_name(fragment)) end else - local primary_index, secondary_index = fragment_primary, fragment_secondary - if fragment_options >= __PRI_WILDCARD_OPTS then - __error_fmt('the pair (%s) is a wildcard and cannot be set', __id_name(fragment)) + __error_fmt('the pair (%s) is a wildcard and cannot be set', + __universal_name(fragment)) end - local fragment_primary_id = __freelist_ids[primary_index] --[[@as evolved.id?]] - if not fragment_primary_id or fragment_primary_id % 2 ^ 20 ~= primary_index then - __error_fmt('the pair (%s) has no alive primary id and cannot be set', __id_name(fragment)) + local fragment_primary_id = __freelist_ids[fragment_primary] --[[@as evolved.id?]] + if not fragment_primary_id or fragment_primary_id % 2 ^ 20 ~= fragment_primary then + __error_fmt('the pair (%s) has no alive primary id and cannot be set', + __universal_name(fragment)) end - local fragment_secondary_id = __freelist_ids[secondary_index] --[[@as evolved.id?]] - if not fragment_secondary_id or fragment_secondary_id % 2 ^ 20 ~= secondary_index then - __error_fmt('the pair (%s) has no alive secondary id and cannot be set', __id_name(fragment)) + local fragment_secondary_id = __freelist_ids[fragment_secondary] --[[@as evolved.id?]] + if not fragment_secondary_id or fragment_secondary_id % 2 ^ 20 ~= fragment_secondary then + __error_fmt('the pair (%s) has no alive secondary id and cannot be set', + __universal_name(fragment)) end end @@ -5109,7 +5250,7 @@ function __evolved_set(entity, fragment, component) if old_chunk_has_setup_hooks or old_chunk_has_assign_hooks then fragment_default, fragment_duplicate, fragment_on_set, fragment_on_assign = - __evolved_get(fragment, __DEFAULT, __DUPLICATE, __ON_SET, __ON_ASSIGN) + __primary_get(fragment, __DEFAULT, __DUPLICATE, __ON_SET, __ON_ASSIGN) end local old_component_index = old_component_indices[fragment] @@ -5179,7 +5320,7 @@ function __evolved_set(entity, fragment, component) if new_chunk_has_setup_hooks or new_chunk_has_insert_hooks then fragment_default, fragment_duplicate, fragment_on_set, fragment_on_insert = - __evolved_get(fragment, __DEFAULT, __DUPLICATE, __ON_SET, __ON_INSERT) + __primary_get(fragment, __DEFAULT, __DUPLICATE, __ON_SET, __ON_INSERT) end local new_place = new_entity_count + 1 @@ -5252,7 +5393,7 @@ function __evolved_set(entity, fragment, component) if new_chunk_has_setup_hooks or new_chunk_has_insert_hooks then req_fragment_default, req_fragment_duplicate, req_fragment_on_set, req_fragment_on_insert = - __evolved_get(req_fragment, __DEFAULT, __DUPLICATE, __ON_SET, __ON_INSERT) + __primary_get(req_fragment, __DEFAULT, __DUPLICATE, __ON_SET, __ON_INSERT) end local req_component_index = new_component_indices[req_fragment] @@ -5315,7 +5456,8 @@ function __evolved_remove(entity, ...) local entity_index, _, entity_options = __evolved_unpack(entity) if entity_options >= __PAIR_OPTS then - __error_fmt('the pair (%s) cannot be changed', __id_name(entity)) + __error_fmt('the pair (%s) cannot be changed', + __universal_name(entity)) elseif __freelist_ids[entity_index] ~= entity then -- the id is not alive, nothing to remove return @@ -5355,7 +5497,7 @@ function __evolved_remove(entity, ...) if not new_fragment_set[fragment] then ---@type evolved.remove_hook? - local fragment_on_remove = __evolved_get(fragment, __ON_REMOVE) + local fragment_on_remove = __primary_get(fragment, __ON_REMOVE) if fragment_on_remove then local old_component_index = old_component_indices[fragment] @@ -5432,7 +5574,8 @@ function __evolved_clear(...) local entity_index, _, entity_options = __evolved_unpack(entity) if entity_options >= __PAIR_OPTS then - __warning_fmt('the pair (%s) cannot be changed', __id_name(entity)) + __warning_fmt('the pair (%s) cannot be changed', + __universal_name(entity)) elseif __freelist_ids[entity_index] ~= entity then -- the id is not alive, nothing to clear else @@ -5449,7 +5592,7 @@ function __evolved_clear(...) local fragment = chunk_fragment_list[chunk_fragment_index] ---@type evolved.remove_hook? - local fragment_on_remove = __evolved_get(fragment, __ON_REMOVE) + local fragment_on_remove = __primary_get(fragment, __ON_REMOVE) if fragment_on_remove then local component_index = chunk_component_indices[fragment] @@ -5510,7 +5653,8 @@ function __evolved_destroy(...) local entity_index, _, entity_options = __evolved_unpack(entity) if entity_options >= __PAIR_OPTS then - __warning_fmt('the pair (%s) cannot be changed', __id_name(entity)) + __warning_fmt('the pair (%s) cannot be changed', + __universal_name(entity)) elseif __freelist_ids[entity_index] ~= entity then -- the id is not alive, nothing to destroy else @@ -5554,36 +5698,39 @@ function __evolved_batch_set(query, fragment, component) local query_index, _, query_options = __evolved_unpack(query) if query_options >= __PAIR_OPTS then - __error_fmt('the pair (%s) cannot be queried', __id_name(query)) + __error_fmt('the pair (%s) cannot be queried', + __universal_name(query)) elseif __freelist_ids[query_index] ~= query then - __error_fmt('the id (%s) is not alive and cannot be queried', __id_name(query)) + __error_fmt('the id (%s) is not alive and cannot be queried', + __universal_name(query)) end local fragment_primary, fragment_secondary, fragment_options = __evolved_unpack(fragment) if fragment_options < __PAIR_OPTS then - local fragment_index = fragment_primary - - if fragment_index == __ANY_INDEX then - __error_fmt('the id (%s) is a wildcard and cannot be set', __id_name(fragment)) - elseif __freelist_ids[fragment_index] ~= fragment then - __error_fmt('the id (%s) is not alive and cannot be set', __id_name(fragment)) + if fragment_primary == __ANY_INDEX then + __error_fmt('the id (%s) is a wildcard and cannot be set', + __universal_name(fragment)) + elseif __freelist_ids[fragment_primary] ~= fragment then + __error_fmt('the id (%s) is not alive and cannot be set', + __universal_name(fragment)) end else - local primary_index, secondary_index = fragment_primary, fragment_secondary - if fragment_options >= __PRI_WILDCARD_OPTS then - __error_fmt('the pair (%s) is a wildcard and cannot be set', __id_name(fragment)) + __error_fmt('the pair (%s) is a wildcard and cannot be set', + __universal_name(fragment)) end - local fragment_primary_id = __freelist_ids[primary_index] --[[@as evolved.id?]] - if not fragment_primary_id or fragment_primary_id % 2 ^ 20 ~= primary_index then - __error_fmt('the pair (%s) has no alive primary id and cannot be set', __id_name(fragment)) + local fragment_primary_id = __freelist_ids[fragment_primary] --[[@as evolved.id?]] + if not fragment_primary_id or fragment_primary_id % 2 ^ 20 ~= fragment_primary then + __error_fmt('the pair (%s) has no alive primary id and cannot be set', + __universal_name(fragment)) end - local fragment_secondary_id = __freelist_ids[secondary_index] --[[@as evolved.id?]] - if not fragment_secondary_id or fragment_secondary_id % 2 ^ 20 ~= secondary_index then - __error_fmt('the pair (%s) has no alive secondary id and cannot be set', __id_name(fragment)) + local fragment_secondary_id = __freelist_ids[fragment_secondary] --[[@as evolved.id?]] + if not fragment_secondary_id or fragment_secondary_id % 2 ^ 20 ~= fragment_secondary then + __error_fmt('the pair (%s) has no alive secondary id and cannot be set', + __universal_name(fragment)) end end @@ -5627,9 +5774,11 @@ function __evolved_batch_remove(query, ...) local query_index, _, query_options = __evolved_unpack(query) if query_options >= __PAIR_OPTS then - __error_fmt('the pair (%s) cannot be queried', __id_name(query)) + __error_fmt('the pair (%s) cannot be queried', + __universal_name(query)) elseif __freelist_ids[query_index] ~= query then - __error_fmt('the id (%s) is not alive and cannot be queried', __id_name(query)) + __error_fmt('the id (%s) is not alive and cannot be queried', + __universal_name(query)) end if __defer_depth > 0 then @@ -5686,9 +5835,11 @@ function __evolved_batch_clear(...) local query_index, _, query_options = __evolved_unpack(query) if query_options >= __PAIR_OPTS then - __warning_fmt('the pair (%s) cannot be queried', __id_name(query)) + __warning_fmt('the pair (%s) cannot be queried', + __universal_name(query)) elseif __freelist_ids[query_index] ~= query then - __warning_fmt('the id (%s) is not alive and cannot be queried', __id_name(query)) + __warning_fmt('the id (%s) is not alive and cannot be queried', + __universal_name(query)) else for chunk in __evolved_execute(query) do chunk_count = chunk_count + 1 @@ -5741,9 +5892,11 @@ function __evolved_batch_destroy(...) local query_index, _, query_options = __evolved_unpack(query) if query_options >= __PAIR_OPTS then - __warning_fmt('the pair (%s) cannot be queried', __id_name(query)) + __warning_fmt('the pair (%s) cannot be queried', + __universal_name(query)) elseif __freelist_ids[query_index] ~= query then - __warning_fmt('the id (%s) is not alive and cannot be queried', __id_name(query)) + __warning_fmt('the id (%s) is not alive and cannot be queried', + __universal_name(query)) else for chunk, entity_list, entity_count in __evolved_execute(query) do clearing_chunk_count = clearing_chunk_count + 1 @@ -5802,18 +5955,16 @@ function __evolved_each(entity) local entity_index, _, entity_options = __evolved_unpack(entity) if entity_options >= __PAIR_OPTS then - __error_fmt('the pair (%s) cannot be iterated', __id_name(entity)) + __error_fmt('the pair (%s) cannot be iterated', + __universal_name(entity)) elseif __freelist_ids[entity_index] ~= entity then - __error_fmt('the id (%s) is not alive and cannot be iterated', __id_name(entity)) + __error_fmt('the id (%s) is not alive and cannot be iterated', + __universal_name(entity)) end - local entity_chunks = __entity_chunks - local entity_places = __entity_places + local entity_chunk = __entity_chunks[entity_index] - local chunk = entity_chunks[entity_index] - local place = entity_places[entity_index] - - if not chunk then + if not entity_chunk then return __iterator_fns.__each_iterator end @@ -5821,8 +5972,8 @@ function __evolved_each(entity) local each_state = __acquire_table(__table_pool_tag.each_state) each_state[1] = __structural_changes - each_state[2] = chunk - each_state[3] = place + each_state[2] = entity_chunk + each_state[3] = __entity_places[entity_index] each_state[4] = 1 return __iterator_fns.__each_iterator, each_state @@ -5836,9 +5987,11 @@ function __evolved_execute(query) local query_index, _, query_options = __evolved_unpack(query) if query_options >= __PAIR_OPTS then - __error_fmt('the pair (%s) cannot be executed', __id_name(query)) + __error_fmt('the pair (%s) cannot be executed', + __universal_name(query)) elseif __freelist_ids[query_index] ~= query then - __error_fmt('the id (%s) is not alive and cannot be executed', __id_name(query)) + __error_fmt('the id (%s) is not alive and cannot be executed', + __universal_name(query)) end ---@type evolved.chunk[] @@ -5858,7 +6011,7 @@ function __evolved_execute(query) if query_include_count > 0 then local query_major = query_include_list[query_include_count] - if __evolved_is_wildcard(query_major) then + if query_major >= __PRI_WILDCARD_OPTS * 2 ^ 40 then local minor_chunks = __minor_chunks[query_major] local minor_chunk_list = minor_chunks and minor_chunks.__item_list local minor_chunk_count = minor_chunks and minor_chunks.__item_count or 0 @@ -5910,7 +6063,7 @@ function __evolved_execute(query) query_include_set[minor_chunk_fragment] or query_include_set[__secondary_wildcard(__evolved_unpack(minor_chunk_fragment))] - if not is_minor_chunk_fragment_included and __evolved_has(minor_chunk_fragment, __EXPLICIT) then + if not is_minor_chunk_fragment_included and __primary_has(minor_chunk_fragment, __EXPLICIT) then is_minor_chunk_matched = false break end @@ -5963,7 +6116,7 @@ function __evolved_execute(query) query_include_set[major_chunk_minor] or query_include_set[__secondary_wildcard(__evolved_unpack(major_chunk_minor))] - if not is_major_chunk_minor_included and __evolved_has(major_chunk_minor, __EXPLICIT) then + if not is_major_chunk_minor_included and __primary_has(major_chunk_minor, __EXPLICIT) then is_major_chunk_matched = false break end @@ -6031,9 +6184,11 @@ function __evolved_process(...) local system_index, _, system_options = __evolved_unpack(system) if system_options >= __PAIR_OPTS then - __warning_fmt('the pair (%s) cannot be processed', __id_name(system)) + __warning_fmt('the pair (%s) cannot be processed', + __universal_name(system)) elseif __freelist_ids[system_index] ~= system then - __warning_fmt('the id (%s) is not alive and cannot be processed', __id_name(system)) + __warning_fmt('the id (%s) is not alive and cannot be processed', + __universal_name(system)) elseif __evolved_has(system, __DISABLED) then -- the system is disabled, nothing to process else @@ -6121,13 +6276,13 @@ function __evolved_pair(primary, secondary) local primary_index, _, primary_options = __evolved_unpack(primary) if primary_options >= __PAIR_OPTS then __error_fmt('the primary id (%s) is a pair and cannot be used as a primary id of a new pair', - __id_name(primary)) + __universal_name(primary)) end local secondary_index, _, secondary_options = __evolved_unpack(secondary) if secondary_options >= __PAIR_OPTS then __error_fmt('the secondary id (%s) is a pair and cannot be used as a secondary id of a new pair', - __id_name(secondary)) + __universal_name(secondary)) end local pair_options = __PAIR_OPTS @@ -6151,19 +6306,22 @@ function __evolved_unpair(pair) local primary_index, secondary_index, pair_options = __evolved_unpack(pair) if pair_options < __PAIR_OPTS then - __error_fmt('the id (%s) is not a pair and cannot be unpaired', __id_name(pair)) + __error_fmt('the id (%s) is not a pair and cannot be unpaired', + __universal_name(pair)) end local pair_primary_id = __freelist_ids[primary_index] --[[@as evolved.id?]] if not pair_primary_id or pair_primary_id % 2 ^ 20 ~= primary_index then - __error_fmt('the pair (%s) has not alive primary id and cannot be unpaired', __id_name(pair)) + __error_fmt('the pair (%s) has not alive primary id and cannot be unpaired', + __universal_name(pair)) else ---@cast pair_primary_id -? end local pair_secondary_id = __freelist_ids[secondary_index] --[[@as evolved.id?]] if not pair_secondary_id or pair_secondary_id % 2 ^ 20 ~= secondary_index then - __error_fmt('the pair (%s) has not alive secondary id and cannot be unpaired', __id_name(pair)) + __error_fmt('the pair (%s) has not alive secondary id and cannot be unpaired', + __universal_name(pair)) else ---@cast pair_secondary_id -? end @@ -6216,7 +6374,8 @@ function __evolved_primary(entity, secondary, index) local secondary_index, _, secondary_options = __evolved_unpack(secondary) if secondary_options >= __PAIR_OPTS then - __error_fmt('the pair (%s) cannot be used as a secondary fragment', __id_name(secondary)) + __error_fmt('the pair (%s) cannot be used as a secondary fragment', + __universal_name(secondary)) end local secondary_fragments = entity_chunk.__secondary_pairs[secondary_index] @@ -6268,7 +6427,8 @@ function __evolved_secondary(entity, primary, index) local primary_index, _, primary_options = __evolved_unpack(primary) if primary_options >= __PAIR_OPTS then - __error_fmt('the pair (%s) cannot be used as a primary fragment', __id_name(primary)) + __error_fmt('the pair (%s) cannot be used as a primary fragment', + __universal_name(primary)) end local primary_fragments = entity_chunk.__primary_pairs[primary_index] @@ -6317,7 +6477,8 @@ function __evolved_primaries(entity, secondary) local secondary_index, _, secondary_options = __evolved_unpack(secondary) if secondary_options >= __PAIR_OPTS then - __error_fmt('the pair (%s) cannot be used as a secondary fragment', __id_name(secondary)) + __error_fmt('the pair (%s) cannot be used as a secondary fragment', + __universal_name(secondary)) end local secondary_fragments = entity_chunk.__secondary_pairs[secondary_index] @@ -6366,7 +6527,8 @@ function __evolved_secondaries(entity, primary) local primary_index, _, primary_options = __evolved_unpack(primary) if primary_options >= __PAIR_OPTS then - __error_fmt('the pair (%s) cannot be used as a primary fragment', __id_name(primary)) + __error_fmt('the pair (%s) cannot be used as a primary fragment', + __universal_name(primary)) end local primary_fragments = entity_chunk.__primary_pairs[primary_index] @@ -6414,7 +6576,8 @@ function __evolved_primary_count(entity, secondary) local secondary_index, _, secondary_options = __evolved_unpack(secondary) if secondary_options >= __PAIR_OPTS then - __error_fmt('the pair (%s) cannot be used as a secondary fragment', __id_name(secondary)) + __error_fmt('the pair (%s) cannot be used as a secondary fragment', + __universal_name(secondary)) end local secondary_fragments = entity_chunk.__secondary_pairs[secondary_index] @@ -6449,7 +6612,8 @@ function __evolved_secondary_count(entity, primary) local primary_index, _, primary_options = __evolved_unpack(primary) if primary_options >= __PAIR_OPTS then - __error_fmt('the pair (%s) cannot be used as a primary fragment', __id_name(primary)) + __error_fmt('the pair (%s) cannot be used as a primary fragment', + __universal_name(primary)) end local primary_fragments = entity_chunk.__primary_pairs[primary_index] @@ -6478,7 +6642,7 @@ function __chunk_mt:__tostring() local fragment_names = {} ---@type string[] for i = 1, self.__fragment_count do - fragment_names[i] = __id_name(self.__fragment_list[i]) + fragment_names[i] = __universal_name(self.__fragment_list[i]) end return __lua_string_format('<%s>', __lua_table_concat(fragment_names, ', ')) @@ -6620,7 +6784,7 @@ function __builder_mt:__tostring() local fragment_names = {} ---@type string[] for i = 1, fragment_count do - fragment_names[i] = __id_name(fragment_list[i]) + fragment_names[i] = __universal_name(fragment_list[i]) end return __lua_string_format('<%s>', __lua_table_concat(fragment_names, ', ')) @@ -6650,21 +6814,21 @@ function __builder_mt:has(fragment) local maybe_has_pairs = primary_pairs and secondary_pairs - if maybe_has_pairs and __evolved_is_wildcard(fragment) then + if maybe_has_pairs and fragment >= __PRI_WILDCARD_OPTS * 2 ^ 40 then ---@cast primary_pairs -? ---@cast secondary_pairs -? - local fragment_primary_index, fragment_secondary_index, fragment_opts = + local fragment_primary, fragment_secondary, fragment_options = __evolved_unpack(fragment) - if fragment_opts == __ANY_WILDCARD_OPTS then + if fragment_options == __ANY_WILDCARD_OPTS then return __lua_next(primary_pairs) ~= nil and __lua_next(secondary_pairs) ~= nil - elseif fragment_opts == __PRI_WILDCARD_OPTS then - local secondary_fragments = secondary_pairs[fragment_secondary_index] + elseif fragment_options == __PRI_WILDCARD_OPTS then + local secondary_fragments = secondary_pairs[fragment_secondary] return secondary_fragments ~= nil and secondary_fragments.__item_count > 0 - elseif fragment_opts == __SEC_WILDCARD_OPTS then - local primary_fragments = primary_pairs[fragment_primary_index] + elseif fragment_options == __SEC_WILDCARD_OPTS then + local primary_fragments = primary_pairs[fragment_primary] return primary_fragments ~= nil and primary_fragments.__item_count > 0 end end @@ -6815,20 +6979,43 @@ end ---@param component evolved.component ---@return evolved.builder builder function __builder_mt:set(fragment, component) - if __debug_mode then - if not __evolved_alive(fragment) then - __error_fmt('the fragment (%s) is not alive and cannot be used', __id_name(fragment)) + local fragment_primary, fragment_secondary, fragment_options = __evolved_unpack(fragment) + + if fragment_options < __PAIR_OPTS then + local fragment_index = fragment_primary + + if fragment_index == __ANY_INDEX then + __error_fmt('the id (%s) is a wildcard and cannot be set', + __universal_name(fragment)) + elseif __freelist_ids[fragment_index] ~= fragment then + __error_fmt('the id (%s) is not alive and cannot be set', + __universal_name(fragment)) + end + else + local primary_index, secondary_index = fragment_primary, fragment_secondary + + if fragment_options >= __PRI_WILDCARD_OPTS then + __error_fmt('the pair (%s) is a wildcard and cannot be set', + __universal_name(fragment)) end - if __evolved_is_wildcard(fragment) then - __error_fmt('the wildcard fragment (%s) cannot be used as a fragment', __id_name(fragment)) + local fragment_primary_id = __freelist_ids[primary_index] --[[@as evolved.id?]] + if not fragment_primary_id or fragment_primary_id % 2 ^ 20 ~= primary_index then + __error_fmt('the pair (%s) has no alive primary id and cannot be set', + __universal_name(fragment)) + end + + local fragment_secondary_id = __freelist_ids[secondary_index] --[[@as evolved.id?]] + if not fragment_secondary_id or fragment_secondary_id % 2 ^ 20 ~= secondary_index then + __error_fmt('the pair (%s) has no alive secondary id and cannot be set', + __universal_name(fragment)) end end do ---@type evolved.default?, evolved.duplicate? local fragment_default, fragment_duplicate = - __evolved_get(fragment, __DEFAULT, __DUPLICATE) + __primary_get(fragment, __DEFAULT, __DUPLICATE) local new_component = component if new_component == nil then new_component = fragment_default end @@ -6838,10 +7025,7 @@ function __builder_mt:set(fragment, component) self.__components[fragment] = new_component end - if __evolved_is_pair(fragment) then - local fragment_primary_index, fragment_secondary_index = - __evolved_unpack(fragment) - + if fragment_options >= __PAIR_OPTS then local primary_pairs = self.__primary_pairs local secondary_pairs = self.__secondary_pairs @@ -6855,19 +7039,19 @@ function __builder_mt:set(fragment, component) self.__secondary_pairs = secondary_pairs end - local primary_fragments = primary_pairs[fragment_primary_index] - local secondary_fragments = secondary_pairs[fragment_secondary_index] + local primary_fragments = primary_pairs[fragment_primary] + local secondary_fragments = secondary_pairs[fragment_secondary] if not primary_fragments then ---@type evolved.assoc_list primary_fragments = __assoc_list_new(4) - primary_pairs[fragment_primary_index] = primary_fragments + primary_pairs[fragment_primary] = primary_fragments end if not secondary_fragments then ---@type evolved.assoc_list secondary_fragments = __assoc_list_new(4) - secondary_pairs[fragment_secondary_index] = secondary_fragments + secondary_pairs[fragment_secondary] = secondary_fragments end __assoc_list_insert(primary_fragments, fragment) @@ -6897,14 +7081,14 @@ function __builder_mt:remove(...) local maybe_has_pairs = primary_pairs and secondary_pairs - if maybe_has_pairs and __evolved_is_wildcard(fragment) then + if maybe_has_pairs and fragment >= __PRI_WILDCARD_OPTS * 2 ^ 40 then ---@cast primary_pairs -? ---@cast secondary_pairs -? - local fragment_primary_index, fragment_secondary_index, fragment_opts = + local fragment_primary, fragment_secondary, fragment_options = __evolved_unpack(fragment) - if fragment_opts == __ANY_WILDCARD_OPTS then + if fragment_options == __ANY_WILDCARD_OPTS then for primary_index, primary_fragments in __lua_next, primary_pairs do local primary_fragment_list = primary_fragments.__item_list local primary_fragment_count = primary_fragments.__item_count @@ -6928,8 +7112,8 @@ function __builder_mt:remove(...) secondary_pairs[secondary_index] = nil end - elseif fragment_opts == __PRI_WILDCARD_OPTS then - local secondary_fragments = secondary_pairs[fragment_secondary_index] + elseif fragment_options == __PRI_WILDCARD_OPTS then + local secondary_fragments = secondary_pairs[fragment_secondary] local secondary_fragment_list = secondary_fragments and secondary_fragments.__item_list local secondary_fragment_count = secondary_fragments and secondary_fragments.__item_count or 0 @@ -6943,9 +7127,9 @@ function __builder_mt:remove(...) end end - secondary_pairs[fragment_secondary_index] = nil - elseif fragment_opts == __SEC_WILDCARD_OPTS then - local primary_fragments = primary_pairs[fragment_primary_index] + secondary_pairs[fragment_secondary] = nil + elseif fragment_options == __SEC_WILDCARD_OPTS then + local primary_fragments = primary_pairs[fragment_primary] local primary_fragment_list = primary_fragments and primary_fragments.__item_list local primary_fragment_count = primary_fragments and primary_fragments.__item_count or 0 @@ -6959,7 +7143,7 @@ function __builder_mt:remove(...) end end - primary_pairs[fragment_primary_index] = nil + primary_pairs[fragment_primary] = nil end end From 46f1516a5520139447ca5259b09da8c72214b1c1 Mon Sep 17 00:00:00 2001 From: BlackMATov Date: Mon, 1 Sep 2025 01:10:17 +0700 Subject: [PATCH 08/12] style fixes --- evolved.lua | 455 ++++++++++++++++++++++++++-------------------------- 1 file changed, 225 insertions(+), 230 deletions(-) diff --git a/evolved.lua b/evolved.lua index 1e3e867..61789e3 100644 --- a/evolved.lua +++ b/evolved.lua @@ -132,10 +132,10 @@ local evolved = { | ANY WILDCARD | RSVD | 11 | 1 | ANY index | ANY index | \------------------------------------------------------------------]=] -local __PAIR_OPTS = 1 -- 0b001 -local __PRI_WILDCARD_OPTS = 3 -- 0b011 -local __SEC_WILDCARD_OPTS = 5 -- 0b101 -local __ANY_WILDCARD_OPTS = 7 -- 0b111 +local __PAIR_OPTIONS = 1 -- 0b001 +local __PRI_WILDCARD_OPTIONS = 3 -- 0b011 +local __SEC_WILDCARD_OPTIONS = 5 -- 0b101 +local __ANY_WILDCARD_OPTIONSS = 7 -- 0b111 --- --- @@ -826,7 +826,7 @@ local __ANY_INDEX = __ANY % 2 ^ 20 --[[@as integer]] local __ANY_WILDCARD = __ANY_INDEX + __ANY_INDEX * 2 ^ 20 - + __ANY_WILDCARD_OPTS * 2 ^ 40 --[[@as evolved.pair]] + + __ANY_WILDCARD_OPTIONSS * 2 ^ 40 --[[@as evolved.pair]] --- --- @@ -965,25 +965,31 @@ local __chunk_get_components local __chunk_required_fragments local __fragment_required_fragments +--- +--- +--- +--- +--- + ---@param id evolved.id | evolved.pair ---@param fragment evolved.fragment ---@return boolean ---@nodiscard function __primary_has(id, fragment) - local id_index, _, id_options = __evolved_unpack(id) + local id_primary, _, id_options = __evolved_unpack(id) - if id_options < __PAIR_OPTS then - if __freelist_ids[id_index] ~= id then + if id_options < __PAIR_OPTIONS then + if __freelist_ids[id_primary] ~= id then return false end else - local id_primary_id = __freelist_ids[id_index] --[[@as evolved.id?]] - if not id_primary_id or id_primary_id % 2 ^ 20 ~= id_index then + local id_primary_id = __freelist_ids[id_primary] --[[@as evolved.id?]] + if not id_primary_id or id_primary_id % 2 ^ 20 ~= id_primary then return false end end - local id_chunk = __entity_chunks[id_index] + local id_chunk = __entity_chunks[id_primary] return id_chunk and __chunk_has_fragment(id_chunk, fragment) or false end @@ -999,20 +1005,20 @@ function __primary_has_all(id, ...) return true end - local id_index, _, id_options = __evolved_unpack(id) + local id_primary, _, id_options = __evolved_unpack(id) - if id_options < __PAIR_OPTS then - if __freelist_ids[id_index] ~= id then + if id_options < __PAIR_OPTIONS then + if __freelist_ids[id_primary] ~= id then return false end else - local id_primary_id = __freelist_ids[id_index] --[[@as evolved.id?]] - if not id_primary_id or id_primary_id % 2 ^ 20 ~= id_index then + local id_primary_id = __freelist_ids[id_primary] --[[@as evolved.id?]] + if not id_primary_id or id_primary_id % 2 ^ 20 ~= id_primary then return false end end - local id_chunk = __entity_chunks[id_index] + local id_chunk = __entity_chunks[id_primary] return id_chunk and __chunk_has_all_fragments(id_chunk, ...) or false end @@ -1028,20 +1034,20 @@ function __primary_has_any(id, ...) return false end - local id_index, _, id_options = __evolved_unpack(id) + local id_primary, _, id_options = __evolved_unpack(id) - if id_options < __PAIR_OPTS then - if __freelist_ids[id_index] ~= id then + if id_options < __PAIR_OPTIONS then + if __freelist_ids[id_primary] ~= id then return false end else - local id_primary_id = __freelist_ids[id_index] --[[@as evolved.id?]] - if not id_primary_id or id_primary_id % 2 ^ 20 ~= id_index then + local id_primary_id = __freelist_ids[id_primary] --[[@as evolved.id?]] + if not id_primary_id or id_primary_id % 2 ^ 20 ~= id_primary then return false end end - local id_chunk = __entity_chunks[id_index] + local id_chunk = __entity_chunks[id_primary] return id_chunk and __chunk_has_any_fragments(id_chunk, ...) or false end @@ -1057,26 +1063,26 @@ function __primary_get(id, ...) return end - local id_index, _, id_options = __evolved_unpack(id) + local id_primary, _, id_options = __evolved_unpack(id) - if id_options < __PAIR_OPTS then - if __freelist_ids[id_index] ~= id then + if id_options < __PAIR_OPTIONS then + if __freelist_ids[id_primary] ~= id then return end else - local id_primary_id = __freelist_ids[id_index] --[[@as evolved.id?]] - if not id_primary_id or id_primary_id % 2 ^ 20 ~= id_index then + local id_primary_id = __freelist_ids[id_primary] --[[@as evolved.id?]] + if not id_primary_id or id_primary_id % 2 ^ 20 ~= id_primary then return end end - local id_chunk = __entity_chunks[id_index] + local id_chunk = __entity_chunks[id_primary] if not id_chunk then return end - local id_place = __entity_places[id_index] + local id_place = __entity_places[id_primary] return __chunk_get_components(id_chunk, id_place, ...) end @@ -1093,7 +1099,7 @@ function __primary_wildcard(secondary) return primary_index + secondary_index * 2 ^ 20 - + __PRI_WILDCARD_OPTS * 2 ^ 40 --[[@as evolved.pair]] + + __PRI_WILDCARD_OPTIONS * 2 ^ 40 --[[@as evolved.pair]] end ---@param primary evolved.id | integer id or index @@ -1109,22 +1115,16 @@ function __secondary_wildcard(primary) return primary_index + secondary_index * 2 ^ 20 - + __SEC_WILDCARD_OPTS * 2 ^ 40 --[[@as evolved.pair]] + + __SEC_WILDCARD_OPTIONS * 2 ^ 40 --[[@as evolved.pair]] end ---- ---- ---- ---- ---- - ---@param id evolved.id ---@return string ---@nodiscard function __universal_name(id) local id_primary, id_secondary, id_options = __evolved_unpack(id) - if id_options < __PAIR_OPTS then + if id_options < __PAIR_OPTIONS then ---@type string? local id_name = __evolved_get(id, __NAME) @@ -1229,13 +1229,13 @@ function __iterator_fns.__execute_major_iterator(execute_state) (not exclude_set or not exclude_set[chunk_child_fragment]) if is_chunk_child_matched and exclude_set and chunk_child.__has_pair_major then - local chunk_child_primary_index, chunk_child_secondary_index = + local chunk_child_fragment_primary, chunk_child_fragment_secondary = __evolved_unpack(chunk_child_fragment) is_chunk_child_matched = not exclude_set[__ANY_WILDCARD] and - not exclude_set[__primary_wildcard(chunk_child_secondary_index)] and - not exclude_set[__secondary_wildcard(chunk_child_primary_index)] + not exclude_set[__primary_wildcard(chunk_child_fragment_secondary)] and + not exclude_set[__secondary_wildcard(chunk_child_fragment_primary)] end if is_chunk_child_matched then @@ -1373,7 +1373,7 @@ local __update_major_chunks_trace ---@return evolved.chunk ---@nodiscard function __new_chunk(chunk_parent, chunk_fragment) - if chunk_fragment >= __PRI_WILDCARD_OPTS * 2 ^ 40 then + if chunk_fragment >= __PRI_WILDCARD_OPTIONS * 2 ^ 40 then __error_fmt('chunk cannot contain wildcard fragments') end @@ -1423,26 +1423,26 @@ function __new_chunk(chunk_parent, chunk_fragment) chunk_fragment_list[chunk_fragment_count] = chunk_fragment end - if chunk_fragment >= __PAIR_OPTS * 2 ^ 40 then + if chunk_fragment >= __PAIR_OPTIONS * 2 ^ 40 then chunk_pair_count = chunk_pair_count + 1 chunk_pair_list[chunk_pair_count] = chunk_fragment - local chunk_primary_index, chunk_secondary_index = + local chunk_fragment_primary, chunk_fragment_secondary = __evolved_unpack(chunk_fragment) - local chunk_primary_fragments = chunk_primary_pairs[chunk_primary_index] - local chunk_secondary_fragments = chunk_secondary_pairs[chunk_secondary_index] + local chunk_primary_fragments = chunk_primary_pairs[chunk_fragment_primary] + local chunk_secondary_fragments = chunk_secondary_pairs[chunk_fragment_secondary] if not chunk_primary_fragments then ---@type evolved.assoc_list chunk_primary_fragments = __assoc_list_new(1) - chunk_primary_pairs[chunk_primary_index] = chunk_primary_fragments + chunk_primary_pairs[chunk_fragment_primary] = chunk_primary_fragments end if not chunk_secondary_fragments then ---@type evolved.assoc_list chunk_secondary_fragments = __assoc_list_new(1) - chunk_secondary_pairs[chunk_secondary_index] = chunk_secondary_fragments + chunk_secondary_pairs[chunk_fragment_secondary] = chunk_secondary_fragments end __assoc_list_insert(chunk_primary_fragments, chunk_fragment) @@ -1536,10 +1536,9 @@ function __new_chunk(chunk_parent, chunk_fragment) __assoc_list_insert(minor_chunks, chunk) end - if chunk_fragment >= __PAIR_OPTS * 2 ^ 40 then + if chunk_fragment >= __PAIR_OPTIONS * 2 ^ 40 then local major = chunk_fragment - local major_primary_index, major_secondary_index = - __evolved_unpack(major) + local major_primary, major_secondary = __evolved_unpack(major) do local major_wildcard = __ANY_WILDCARD @@ -1555,7 +1554,7 @@ function __new_chunk(chunk_parent, chunk_fragment) end do - local major_wildcard = __secondary_wildcard(major_primary_index) + local major_wildcard = __secondary_wildcard(major_primary) local major_wildcard_chunks = __major_chunks[major_wildcard] if not major_wildcard_chunks then @@ -1568,7 +1567,7 @@ function __new_chunk(chunk_parent, chunk_fragment) end do - local major_wildcard = __primary_wildcard(major_secondary_index) + local major_wildcard = __primary_wildcard(major_secondary) local major_wildcard_chunks = __major_chunks[major_wildcard] if not major_wildcard_chunks then @@ -1596,11 +1595,10 @@ function __new_chunk(chunk_parent, chunk_fragment) for i = 1, chunk_pair_count do local minor = chunk_pair_list[i] - local minor_primary_index, minor_secondary_index = - __evolved_unpack(minor) + local minor_primary, minor_secondary = __evolved_unpack(minor) do - local minor_wildcard = __secondary_wildcard(minor_primary_index) + local minor_wildcard = __secondary_wildcard(minor_primary) local minor_wildcard_chunks = __minor_chunks[minor_wildcard] if not minor_wildcard_chunks then @@ -1613,7 +1611,7 @@ function __new_chunk(chunk_parent, chunk_fragment) end do - local minor_wildcard = __primary_wildcard(minor_secondary_index) + local minor_wildcard = __primary_wildcard(minor_secondary) local minor_wildcard_chunks = __minor_chunks[minor_wildcard] if not minor_wildcard_chunks then @@ -1713,7 +1711,7 @@ function __update_chunk_flags(chunk) local has_remove_hooks = (chunk_parent ~= nil and chunk_parent.__has_remove_hooks) or __primary_has(chunk_fragment, __ON_REMOVE) - local has_pair_major = chunk_fragment >= __PAIR_OPTS * 2 ^ 40 + local has_pair_major = chunk_fragment >= __PAIR_OPTIONS * 2 ^ 40 local has_pair_minors = chunk_parent ~= nil and chunk_parent.__has_pair_fragments local has_pair_fragments = has_pair_major or has_pair_minors @@ -1760,7 +1758,7 @@ end ---@param trace fun(chunk: evolved.chunk, ...: any) ---@param ... any additional trace arguments function __trace_major_chunks(major, trace, ...) - if major >= __PAIR_OPTS * 2 ^ 40 then + if major >= __PAIR_OPTIONS * 2 ^ 40 then __error_fmt('trace operations on pair fragments are not supported') end @@ -1837,7 +1835,7 @@ end ---@param trace fun(chunk: evolved.chunk, ...: any) ---@param ... any additional trace arguments function __trace_minor_chunks(minor, trace, ...) - if minor >= __PAIR_OPTS * 2 ^ 40 then + if minor >= __PAIR_OPTIONS * 2 ^ 40 then __error_fmt('trace operations on pair fragments are not supported') end @@ -1993,11 +1991,11 @@ function __chunk_without_fragment(chunk, fragment) if without_fragment_edge then return without_fragment_edge end end - if chunk.__has_pair_fragments and fragment >= __PRI_WILDCARD_OPTS * 2 ^ 40 then + if chunk.__has_pair_fragments and fragment >= __PRI_WILDCARD_OPTIONS * 2 ^ 40 then local fragment_primary, fragment_secondary, fragment_options = __evolved_unpack(fragment) - if fragment_options == __ANY_WILDCARD_OPTS then + if fragment_options == __ANY_WILDCARD_OPTIONSS then while chunk and chunk.__has_pair_major do chunk = chunk.__parent end @@ -2018,7 +2016,7 @@ function __chunk_without_fragment(chunk, fragment) end return sib_chunk - elseif fragment_options == __PRI_WILDCARD_OPTS then + elseif fragment_options == __PRI_WILDCARD_OPTIONS then if not chunk.__secondary_pairs[fragment_secondary] then -- the chunk does not have such pairs return chunk @@ -2037,8 +2035,8 @@ function __chunk_without_fragment(chunk, fragment) for ini_pair_index = lst_pair_index, ini_pair_count do local ini_pair = ini_pair_list[ini_pair_index] - local _, ini_secondary_index = __evolved_unpack(ini_pair) - if ini_secondary_index ~= fragment_secondary then + local _, ini_pair_secondary = __evolved_unpack(ini_pair) + if ini_pair_secondary ~= fragment_secondary then sib_chunk = __chunk_with_fragment(sib_chunk, ini_pair) end end @@ -2049,7 +2047,7 @@ function __chunk_without_fragment(chunk, fragment) end return sib_chunk - elseif fragment_options == __SEC_WILDCARD_OPTS then + elseif fragment_options == __SEC_WILDCARD_OPTIONS then if not chunk.__primary_pairs[fragment_primary] then -- the chunk does not have such pairs return chunk @@ -2068,8 +2066,8 @@ function __chunk_without_fragment(chunk, fragment) for ini_pair_index = lst_pair_index, ini_pair_count do local ini_pair = ini_pair_list[ini_pair_index] - local ini_primary_index, _ = __evolved_unpack(ini_pair) - if ini_primary_index ~= fragment_primary then + local ini_pair_primary, _ = __evolved_unpack(ini_pair) + if ini_pair_primary ~= fragment_primary then sib_chunk = __chunk_with_fragment(sib_chunk, ini_pair) end end @@ -2230,16 +2228,16 @@ function __chunk_has_fragment(chunk, fragment) return true end - if chunk.__has_pair_fragments and fragment >= __PRI_WILDCARD_OPTS * 2 ^ 40 then + if chunk.__has_pair_fragments and fragment >= __PRI_WILDCARD_OPTIONS * 2 ^ 40 then local fragment_primary, fragment_secondary, fragment_options = __evolved_unpack(fragment) - if fragment_options == __ANY_WILDCARD_OPTS then + if fragment_options == __ANY_WILDCARD_OPTIONSS then return true - elseif fragment_options == __PRI_WILDCARD_OPTS then + elseif fragment_options == __PRI_WILDCARD_OPTIONS then local secondary_fragments = chunk.__secondary_pairs[fragment_secondary] return secondary_fragments and secondary_fragments.__item_count > 0 - elseif fragment_options == __SEC_WILDCARD_OPTS then + elseif fragment_options == __SEC_WILDCARD_OPTIONS then local primary_fragments = chunk.__primary_pairs[fragment_primary] return primary_fragments and primary_fragments.__item_count > 0 end @@ -2513,7 +2511,7 @@ function __chunk_required_fragments(chunk, req_fragment_set, req_fragment_list, fragment_stack[fragment_stack_size] = nil fragment_stack_size = fragment_stack_size - 1 - if stack_fragment >= __PAIR_OPTS * 2 ^ 40 then + if stack_fragment >= __PAIR_OPTIONS * 2 ^ 40 then stack_fragment = __evolved_unpair(stack_fragment) end @@ -2563,7 +2561,7 @@ function __fragment_required_fragments(fragment, req_fragment_set, req_fragment_ fragment_stack[fragment_stack_size] = nil fragment_stack_size = fragment_stack_size - 1 - if stack_fragment >= __PAIR_OPTS * 2 ^ 40 then + if stack_fragment >= __PAIR_OPTIONS * 2 ^ 40 then stack_fragment = __evolved_unpair(stack_fragment) end @@ -4958,7 +4956,7 @@ end function __evolved_alive(entity) local entity_primary, entity_secondary, entity_options = __evolved_unpack(entity) - if entity_options < __PAIR_OPTS then + if entity_options < __PAIR_OPTIONS then if __freelist_ids[entity_primary] ~= entity then return false end @@ -5025,7 +5023,7 @@ end function __evolved_empty(entity) local entity_primary, _, entity_options = __evolved_unpack(entity) - if entity_options >= __PAIR_OPTS then + if entity_options >= __PAIR_OPTIONS then return true end @@ -5083,17 +5081,17 @@ end ---@return boolean ---@nodiscard function __evolved_has(entity, fragment) - local entity_index, _, entity_options = __evolved_unpack(entity) + local entity_primary, _, entity_options = __evolved_unpack(entity) - if entity_options >= __PAIR_OPTS then + if entity_options >= __PAIR_OPTIONS then return false end - if __freelist_ids[entity_index] ~= entity then + if __freelist_ids[entity_primary] ~= entity then return false end - local entity_chunk = __entity_chunks[entity_index] + local entity_chunk = __entity_chunks[entity_primary] return entity_chunk and __chunk_has_fragment(entity_chunk, fragment) or false end @@ -5109,17 +5107,17 @@ function __evolved_has_all(entity, ...) return true end - local entity_index, _, entity_options = __evolved_unpack(entity) + local entity_primary, _, entity_options = __evolved_unpack(entity) - if entity_options >= __PAIR_OPTS then + if entity_options >= __PAIR_OPTIONS then return false end - if __freelist_ids[entity_index] ~= entity then + if __freelist_ids[entity_primary] ~= entity then return false end - local entity_chunk = __entity_chunks[entity_index] + local entity_chunk = __entity_chunks[entity_primary] return entity_chunk and __chunk_has_all_fragments(entity_chunk, ...) or false end @@ -5135,17 +5133,17 @@ function __evolved_has_any(entity, ...) return false end - local entity_index, _, entity_options = __evolved_unpack(entity) + local entity_primary, _, entity_options = __evolved_unpack(entity) - if entity_options >= __PAIR_OPTS then + if entity_options >= __PAIR_OPTIONS then return false end - if __freelist_ids[entity_index] ~= entity then + if __freelist_ids[entity_primary] ~= entity then return false end - local entity_chunk = __entity_chunks[entity_index] + local entity_chunk = __entity_chunks[entity_primary] return entity_chunk and __chunk_has_any_fragments(entity_chunk, ...) or false end @@ -5155,23 +5153,23 @@ end ---@return evolved.component ... components ---@nodiscard function __evolved_get(entity, ...) - local entity_index, _, entity_options = __evolved_unpack(entity) + local entity_primary, _, entity_options = __evolved_unpack(entity) - if entity_options >= __PAIR_OPTS then + if entity_options >= __PAIR_OPTIONS then return end - if __freelist_ids[entity_index] ~= entity then + if __freelist_ids[entity_primary] ~= entity then return end - local entity_chunk = __entity_chunks[entity_index] + local entity_chunk = __entity_chunks[entity_primary] if not entity_chunk then return end - local entity_place = __entity_places[entity_index] + local entity_place = __entity_places[entity_primary] return __chunk_get_components(entity_chunk, entity_place, ...) end @@ -5179,12 +5177,12 @@ end ---@param fragment evolved.fragment ---@param component evolved.component function __evolved_set(entity, fragment, component) - local entity_index, _, entity_options = __evolved_unpack(entity) + local entity_primary, _, entity_options = __evolved_unpack(entity) - if entity_options >= __PAIR_OPTS then + if entity_options >= __PAIR_OPTIONS then __error_fmt('the pair (%s) cannot be changed', __universal_name(entity)) - elseif __freelist_ids[entity_index] ~= entity then + elseif __freelist_ids[entity_primary] ~= entity then __error_fmt('the id (%s) is not alive and cannot be changed', __universal_name(entity)) end @@ -5192,7 +5190,7 @@ function __evolved_set(entity, fragment, component) local fragment_primary, fragment_secondary, fragment_options = __evolved_unpack(fragment) - if fragment_options < __PAIR_OPTS then + if fragment_options < __PAIR_OPTIONS then if fragment_primary == __ANY_INDEX then __error_fmt('the id (%s) is a wildcard and cannot be set', __universal_name(fragment)) @@ -5201,7 +5199,7 @@ function __evolved_set(entity, fragment, component) __universal_name(fragment)) end else - if fragment_options >= __PRI_WILDCARD_OPTS then + if fragment_options >= __PRI_WILDCARD_OPTIONS then __error_fmt('the pair (%s) is a wildcard and cannot be set', __universal_name(fragment)) end @@ -5227,8 +5225,8 @@ function __evolved_set(entity, fragment, component) local entity_chunks = __entity_chunks local entity_places = __entity_places - local old_chunk = entity_chunks[entity_index] - local old_place = entity_places[entity_index] + local old_chunk = entity_chunks[entity_primary] + local old_place = entity_places[entity_primary] local new_chunk = __chunk_with_fragment(old_chunk, fragment) @@ -5345,8 +5343,8 @@ function __evolved_set(entity, fragment, component) end do - entity_chunks[entity_index] = new_chunk - entity_places[entity_index] = new_place + entity_chunks[entity_primary] = new_chunk + entity_places[entity_primary] = new_place __structural_changes = __structural_changes + 1 end @@ -5453,12 +5451,12 @@ function __evolved_remove(entity, ...) return end - local entity_index, _, entity_options = __evolved_unpack(entity) + local entity_primary, _, entity_options = __evolved_unpack(entity) - if entity_options >= __PAIR_OPTS then + if entity_options >= __PAIR_OPTIONS then __error_fmt('the pair (%s) cannot be changed', __universal_name(entity)) - elseif __freelist_ids[entity_index] ~= entity then + elseif __freelist_ids[entity_primary] ~= entity then -- the id is not alive, nothing to remove return end @@ -5471,8 +5469,8 @@ function __evolved_remove(entity, ...) local entity_chunks = __entity_chunks local entity_places = __entity_places - local old_chunk = entity_chunks[entity_index] - local old_place = entity_places[entity_index] + local old_chunk = entity_chunks[entity_primary] + local old_place = entity_places[entity_primary] local new_chunk = __chunk_without_fragments(old_chunk, ...) @@ -5539,8 +5537,8 @@ function __evolved_remove(entity, ...) do __detach_entity(old_chunk, old_place) - entity_chunks[entity_index] = new_chunk - entity_places[entity_index] = new_chunk and new_chunk.__entity_count + entity_chunks[entity_primary] = new_chunk + entity_places[entity_primary] = new_chunk and new_chunk.__entity_count __structural_changes = __structural_changes + 1 end @@ -5571,16 +5569,16 @@ function __evolved_clear(...) for argument_index = 1, argument_count do ---@type evolved.entity local entity = __lua_select(argument_index, ...) - local entity_index, _, entity_options = __evolved_unpack(entity) + local entity_primary, _, entity_options = __evolved_unpack(entity) - if entity_options >= __PAIR_OPTS then + if entity_options >= __PAIR_OPTIONS then __warning_fmt('the pair (%s) cannot be changed', __universal_name(entity)) - elseif __freelist_ids[entity_index] ~= entity then + elseif __freelist_ids[entity_primary] ~= entity then -- the id is not alive, nothing to clear else - local chunk = entity_chunks[entity_index] - local place = entity_places[entity_index] + local chunk = entity_chunks[entity_primary] + local place = entity_places[entity_primary] if chunk and chunk.__has_remove_hooks then local chunk_fragment_list = chunk.__fragment_list @@ -5611,8 +5609,8 @@ function __evolved_clear(...) if chunk then __detach_entity(chunk, place) - entity_chunks[entity_index] = nil - entity_places[entity_index] = nil + entity_chunks[entity_primary] = nil + entity_places[entity_primary] = nil __structural_changes = __structural_changes + 1 end @@ -5650,12 +5648,12 @@ function __evolved_destroy(...) for argument_index = 1, argument_count do ---@type evolved.entity local entity = __lua_select(argument_index, ...) - local entity_index, _, entity_options = __evolved_unpack(entity) + local entity_primary, _, entity_options = __evolved_unpack(entity) - if entity_options >= __PAIR_OPTS then + if entity_options >= __PAIR_OPTIONS then __warning_fmt('the pair (%s) cannot be changed', __universal_name(entity)) - elseif __freelist_ids[entity_index] ~= entity then + elseif __freelist_ids[entity_primary] ~= entity then -- the id is not alive, nothing to destroy else local is_fragment = @@ -5695,19 +5693,20 @@ end ---@param fragment evolved.fragment ---@param component evolved.component function __evolved_batch_set(query, fragment, component) - local query_index, _, query_options = __evolved_unpack(query) + local query_primary, _, query_options = __evolved_unpack(query) - if query_options >= __PAIR_OPTS then + if query_options >= __PAIR_OPTIONS then __error_fmt('the pair (%s) cannot be queried', __universal_name(query)) - elseif __freelist_ids[query_index] ~= query then + elseif __freelist_ids[query_primary] ~= query then __error_fmt('the id (%s) is not alive and cannot be queried', __universal_name(query)) end - local fragment_primary, fragment_secondary, fragment_options = __evolved_unpack(fragment) + local fragment_primary, fragment_secondary, fragment_options = + __evolved_unpack(fragment) - if fragment_options < __PAIR_OPTS then + if fragment_options < __PAIR_OPTIONS then if fragment_primary == __ANY_INDEX then __error_fmt('the id (%s) is a wildcard and cannot be set', __universal_name(fragment)) @@ -5716,7 +5715,7 @@ function __evolved_batch_set(query, fragment, component) __universal_name(fragment)) end else - if fragment_options >= __PRI_WILDCARD_OPTS then + if fragment_options >= __PRI_WILDCARD_OPTIONS then __error_fmt('the pair (%s) is a wildcard and cannot be set', __universal_name(fragment)) end @@ -5771,12 +5770,12 @@ function __evolved_batch_remove(query, ...) return end - local query_index, _, query_options = __evolved_unpack(query) + local query_primary, _, query_options = __evolved_unpack(query) - if query_options >= __PAIR_OPTS then + if query_options >= __PAIR_OPTIONS then __error_fmt('the pair (%s) cannot be queried', __universal_name(query)) - elseif __freelist_ids[query_index] ~= query then + elseif __freelist_ids[query_primary] ~= query then __error_fmt('the id (%s) is not alive and cannot be queried', __universal_name(query)) end @@ -5832,12 +5831,12 @@ function __evolved_batch_clear(...) for argument_index = 1, argument_count do ---@type evolved.query local query = __lua_select(argument_index, ...) - local query_index, _, query_options = __evolved_unpack(query) + local query_primary, _, query_options = __evolved_unpack(query) - if query_options >= __PAIR_OPTS then + if query_options >= __PAIR_OPTIONS then __warning_fmt('the pair (%s) cannot be queried', __universal_name(query)) - elseif __freelist_ids[query_index] ~= query then + elseif __freelist_ids[query_primary] ~= query then __warning_fmt('the id (%s) is not alive and cannot be queried', __universal_name(query)) else @@ -5889,12 +5888,12 @@ function __evolved_batch_destroy(...) for argument_index = 1, argument_count do ---@type evolved.query local query = __lua_select(argument_index, ...) - local query_index, _, query_options = __evolved_unpack(query) + local query_primary, _, query_options = __evolved_unpack(query) - if query_options >= __PAIR_OPTS then + if query_options >= __PAIR_OPTIONS then __warning_fmt('the pair (%s) cannot be queried', __universal_name(query)) - elseif __freelist_ids[query_index] ~= query then + elseif __freelist_ids[query_primary] ~= query then __warning_fmt('the id (%s) is not alive and cannot be queried', __universal_name(query)) else @@ -5952,17 +5951,17 @@ end ---@return evolved.each_state? iterator_state ---@nodiscard function __evolved_each(entity) - local entity_index, _, entity_options = __evolved_unpack(entity) + local entity_primary, _, entity_options = __evolved_unpack(entity) - if entity_options >= __PAIR_OPTS then + if entity_options >= __PAIR_OPTIONS then __error_fmt('the pair (%s) cannot be iterated', __universal_name(entity)) - elseif __freelist_ids[entity_index] ~= entity then + elseif __freelist_ids[entity_primary] ~= entity then __error_fmt('the id (%s) is not alive and cannot be iterated', __universal_name(entity)) end - local entity_chunk = __entity_chunks[entity_index] + local entity_chunk = __entity_chunks[entity_primary] if not entity_chunk then return __iterator_fns.__each_iterator @@ -5973,7 +5972,7 @@ function __evolved_each(entity) each_state[1] = __structural_changes each_state[2] = entity_chunk - each_state[3] = __entity_places[entity_index] + each_state[3] = __entity_places[entity_primary] each_state[4] = 1 return __iterator_fns.__each_iterator, each_state @@ -5984,12 +5983,12 @@ end ---@return evolved.execute_state? iterator_state ---@nodiscard function __evolved_execute(query) - local query_index, _, query_options = __evolved_unpack(query) + local query_primary, _, query_options = __evolved_unpack(query) - if query_options >= __PAIR_OPTS then + if query_options >= __PAIR_OPTIONS then __error_fmt('the pair (%s) cannot be executed', __universal_name(query)) - elseif __freelist_ids[query_index] ~= query then + elseif __freelist_ids[query_primary] ~= query then __error_fmt('the id (%s) is not alive and cannot be executed', __universal_name(query)) end @@ -6011,7 +6010,7 @@ function __evolved_execute(query) if query_include_count > 0 then local query_major = query_include_list[query_include_count] - if query_major >= __PRI_WILDCARD_OPTS * 2 ^ 40 then + if query_major >= __PRI_WILDCARD_OPTIONS * 2 ^ 40 then local minor_chunks = __minor_chunks[query_major] local minor_chunk_list = minor_chunks and minor_chunks.__item_list local minor_chunk_count = minor_chunks and minor_chunks.__item_count or 0 @@ -6181,12 +6180,12 @@ function __evolved_process(...) for argument_index = 1, argument_count do ---@type evolved.system local system = __lua_select(argument_index, ...) - local system_index, _, system_options = __evolved_unpack(system) + local system_primary, _, system_options = __evolved_unpack(system) - if system_options >= __PAIR_OPTS then + if system_options >= __PAIR_OPTIONS then __warning_fmt('the pair (%s) cannot be processed', __universal_name(system)) - elseif __freelist_ids[system_index] ~= system then + elseif __freelist_ids[system_primary] ~= system then __warning_fmt('the id (%s) is not alive and cannot be processed', __universal_name(system)) elseif __evolved_has(system, __DISABLED) then @@ -6274,25 +6273,25 @@ end ---@nodiscard function __evolved_pair(primary, secondary) local primary_index, _, primary_options = __evolved_unpack(primary) - if primary_options >= __PAIR_OPTS then + if primary_options >= __PAIR_OPTIONS then __error_fmt('the primary id (%s) is a pair and cannot be used as a primary id of a new pair', __universal_name(primary)) end local secondary_index, _, secondary_options = __evolved_unpack(secondary) - if secondary_options >= __PAIR_OPTS then + if secondary_options >= __PAIR_OPTIONS then __error_fmt('the secondary id (%s) is a pair and cannot be used as a secondary id of a new pair', __universal_name(secondary)) end - local pair_options = __PAIR_OPTS + local pair_options = __PAIR_OPTIONS if primary_index == __ANY_INDEX and secondary_index == __ANY_INDEX then - pair_options = __ANY_WILDCARD_OPTS + pair_options = __ANY_WILDCARD_OPTIONSS elseif primary_index == __ANY_INDEX then - pair_options = __PRI_WILDCARD_OPTS + pair_options = __PRI_WILDCARD_OPTIONS elseif secondary_index == __ANY_INDEX then - pair_options = __SEC_WILDCARD_OPTS + pair_options = __SEC_WILDCARD_OPTIONS end return primary_index + secondary_index * 2 ^ 20 + pair_options * 2 ^ 40 --[[@as evolved.pair]] @@ -6303,23 +6302,22 @@ end ---@return evolved.id secondary ---@nodiscard function __evolved_unpair(pair) - local primary_index, secondary_index, pair_options = __evolved_unpack(pair) - - if pair_options < __PAIR_OPTS then + local pair_primary, pair_secondary, pair_options = __evolved_unpack(pair) + if pair_options < __PAIR_OPTIONS then __error_fmt('the id (%s) is not a pair and cannot be unpaired', __universal_name(pair)) end - local pair_primary_id = __freelist_ids[primary_index] --[[@as evolved.id?]] - if not pair_primary_id or pair_primary_id % 2 ^ 20 ~= primary_index then + local pair_primary_id = __freelist_ids[pair_primary] --[[@as evolved.id?]] + if not pair_primary_id or pair_primary_id % 2 ^ 20 ~= pair_primary then __error_fmt('the pair (%s) has not alive primary id and cannot be unpaired', __universal_name(pair)) else ---@cast pair_primary_id -? end - local pair_secondary_id = __freelist_ids[secondary_index] --[[@as evolved.id?]] - if not pair_secondary_id or pair_secondary_id % 2 ^ 20 ~= secondary_index then + local pair_secondary_id = __freelist_ids[pair_secondary] --[[@as evolved.id?]] + if not pair_secondary_id or pair_secondary_id % 2 ^ 20 ~= pair_secondary then __error_fmt('the pair (%s) has not alive secondary id and cannot be unpaired', __universal_name(pair)) else @@ -6333,14 +6331,14 @@ end ---@return boolean ---@nodiscard function __evolved_is_pair(id) - return id >= __PAIR_OPTS * 2 ^ 40 + return id >= __PAIR_OPTIONS * 2 ^ 40 end ---@param id evolved.id ---@return boolean ---@nodiscard function __evolved_is_wildcard(id) - return id >= __PRI_WILDCARD_OPTS * 2 ^ 40 + return id >= __PRI_WILDCARD_OPTIONS * 2 ^ 40 end ---@param entity evolved.entity @@ -6352,20 +6350,20 @@ end function __evolved_primary(entity, secondary, index) index = index or 1 - local entity_index, _, entity_options = __evolved_unpack(entity) + local entity_primary, _, entity_options = __evolved_unpack(entity) - if entity_options < __PAIR_OPTS then - if __freelist_ids[entity_index] ~= entity then + if entity_options < __PAIR_OPTIONS then + if __freelist_ids[entity_primary] ~= entity then return end else - local entity_primary_id = __freelist_ids[entity_index] --[[@as evolved.id?]] - if not entity_primary_id or entity_primary_id % 2 ^ 20 ~= entity_index then + local entity_primary_id = __freelist_ids[entity_primary] --[[@as evolved.id?]] + if not entity_primary_id or entity_primary_id % 2 ^ 20 ~= entity_primary then return end end - local entity_chunk = __entity_chunks[entity_index] + local entity_chunk = __entity_chunks[entity_primary] if not entity_chunk then return @@ -6373,7 +6371,7 @@ function __evolved_primary(entity, secondary, index) local secondary_index, _, secondary_options = __evolved_unpack(secondary) - if secondary_options >= __PAIR_OPTS then + if secondary_options >= __PAIR_OPTIONS then __error_fmt('the pair (%s) cannot be used as a secondary fragment', __universal_name(secondary)) end @@ -6392,7 +6390,7 @@ function __evolved_primary(entity, secondary, index) local component_index = entity_chunk.__component_indices[secondary_fragment] local component_storage = entity_chunk.__component_storages[component_index] - local entity_place = __entity_places[entity_index] + local entity_place = __entity_places[entity_primary] return primary, component_storage and component_storage[entity_place] end @@ -6405,20 +6403,20 @@ end function __evolved_secondary(entity, primary, index) index = index or 1 - local entity_index, _, entity_options = __evolved_unpack(entity) + local entity_primary, _, entity_options = __evolved_unpack(entity) - if entity_options < __PAIR_OPTS then - if __freelist_ids[entity_index] ~= entity then + if entity_options < __PAIR_OPTIONS then + if __freelist_ids[entity_primary] ~= entity then return end else - local entity_primary_id = __freelist_ids[entity_index] --[[@as evolved.id?]] - if not entity_primary_id or entity_primary_id % 2 ^ 20 ~= entity_index then + local entity_primary_id = __freelist_ids[entity_primary] --[[@as evolved.id?]] + if not entity_primary_id or entity_primary_id % 2 ^ 20 ~= entity_primary then return end end - local entity_chunk = __entity_chunks[entity_index] + local entity_chunk = __entity_chunks[entity_primary] if not entity_chunk then return @@ -6426,7 +6424,7 @@ function __evolved_secondary(entity, primary, index) local primary_index, _, primary_options = __evolved_unpack(primary) - if primary_options >= __PAIR_OPTS then + if primary_options >= __PAIR_OPTIONS then __error_fmt('the pair (%s) cannot be used as a primary fragment', __universal_name(primary)) end @@ -6445,7 +6443,7 @@ function __evolved_secondary(entity, primary, index) local component_index = entity_chunk.__component_indices[primary_fragment] local component_storage = entity_chunk.__component_storages[component_index] - local entity_place = __entity_places[entity_index] + local entity_place = __entity_places[entity_primary] return secondary, component_storage and component_storage[entity_place] end @@ -6455,20 +6453,20 @@ end ---@return evolved.primaries_state? iterator_state ---@nodiscard function __evolved_primaries(entity, secondary) - local entity_index, _, entity_options = __evolved_unpack(entity) + local entity_primary, _, entity_options = __evolved_unpack(entity) - if entity_options < __PAIR_OPTS then - if __freelist_ids[entity_index] ~= entity then + if entity_options < __PAIR_OPTIONS then + if __freelist_ids[entity_primary] ~= entity then return __iterator_fns.__primaries_iterator end else - local entity_primary_id = __freelist_ids[entity_index] --[[@as evolved.id?]] - if not entity_primary_id or entity_primary_id % 2 ^ 20 ~= entity_index then + local entity_primary_id = __freelist_ids[entity_primary] --[[@as evolved.id?]] + if not entity_primary_id or entity_primary_id % 2 ^ 20 ~= entity_primary then return __iterator_fns.__primaries_iterator end end - local entity_chunk = __entity_chunks[entity_index] + local entity_chunk = __entity_chunks[entity_primary] if not entity_chunk then return __iterator_fns.__primaries_iterator @@ -6476,7 +6474,7 @@ function __evolved_primaries(entity, secondary) local secondary_index, _, secondary_options = __evolved_unpack(secondary) - if secondary_options >= __PAIR_OPTS then + if secondary_options >= __PAIR_OPTIONS then __error_fmt('the pair (%s) cannot be used as a secondary fragment', __universal_name(secondary)) end @@ -6492,7 +6490,7 @@ function __evolved_primaries(entity, secondary) primaries_state[1] = __structural_changes primaries_state[2] = entity_chunk - primaries_state[3] = __entity_places[entity_index] + primaries_state[3] = __entity_places[entity_primary] primaries_state[4] = secondary_index primaries_state[5] = 1 @@ -6505,20 +6503,20 @@ end ---@return evolved.secondaries_state? iterator_state ---@nodiscard function __evolved_secondaries(entity, primary) - local entity_index, _, entity_options = __evolved_unpack(entity) + local entity_primary, _, entity_options = __evolved_unpack(entity) - if entity_options < __PAIR_OPTS then - if __freelist_ids[entity_index] ~= entity then + if entity_options < __PAIR_OPTIONS then + if __freelist_ids[entity_primary] ~= entity then return __iterator_fns.__secondaries_iterator end else - local entity_primary_id = __freelist_ids[entity_index] --[[@as evolved.id?]] - if not entity_primary_id or entity_primary_id % 2 ^ 20 ~= entity_index then + local entity_primary_id = __freelist_ids[entity_primary] --[[@as evolved.id?]] + if not entity_primary_id or entity_primary_id % 2 ^ 20 ~= entity_primary then return __iterator_fns.__secondaries_iterator end end - local entity_chunk = __entity_chunks[entity_index] + local entity_chunk = __entity_chunks[entity_primary] if not entity_chunk then return __iterator_fns.__secondaries_iterator @@ -6526,7 +6524,7 @@ function __evolved_secondaries(entity, primary) local primary_index, _, primary_options = __evolved_unpack(primary) - if primary_options >= __PAIR_OPTS then + if primary_options >= __PAIR_OPTIONS then __error_fmt('the pair (%s) cannot be used as a primary fragment', __universal_name(primary)) end @@ -6542,7 +6540,7 @@ function __evolved_secondaries(entity, primary) secondaries_state[1] = __structural_changes secondaries_state[2] = entity_chunk - secondaries_state[3] = __entity_places[entity_index] + secondaries_state[3] = __entity_places[entity_primary] secondaries_state[4] = primary_index secondaries_state[5] = 1 @@ -6554,20 +6552,20 @@ end ---@return integer ---@nodiscard function __evolved_primary_count(entity, secondary) - local entity_index, _, entity_options = __evolved_unpack(entity) + local entity_primary, _, entity_options = __evolved_unpack(entity) - if entity_options < __PAIR_OPTS then - if __freelist_ids[entity_index] ~= entity then + if entity_options < __PAIR_OPTIONS then + if __freelist_ids[entity_primary] ~= entity then return 0 end else - local entity_primary_id = __freelist_ids[entity_index] --[[@as evolved.id?]] - if not entity_primary_id or entity_primary_id % 2 ^ 20 ~= entity_index then + local entity_primary_id = __freelist_ids[entity_primary] --[[@as evolved.id?]] + if not entity_primary_id or entity_primary_id % 2 ^ 20 ~= entity_primary then return 0 end end - local entity_chunk = __entity_chunks[entity_index] + local entity_chunk = __entity_chunks[entity_primary] if not entity_chunk then return 0 @@ -6575,7 +6573,7 @@ function __evolved_primary_count(entity, secondary) local secondary_index, _, secondary_options = __evolved_unpack(secondary) - if secondary_options >= __PAIR_OPTS then + if secondary_options >= __PAIR_OPTIONS then __error_fmt('the pair (%s) cannot be used as a secondary fragment', __universal_name(secondary)) end @@ -6590,20 +6588,20 @@ end ---@return integer ---@nodiscard function __evolved_secondary_count(entity, primary) - local entity_index, _, entity_options = __evolved_unpack(entity) + local entity_primary, _, entity_options = __evolved_unpack(entity) - if entity_options < __PAIR_OPTS then - if __freelist_ids[entity_index] ~= entity then + if entity_options < __PAIR_OPTIONS then + if __freelist_ids[entity_primary] ~= entity then return 0 end else - local entity_primary_id = __freelist_ids[entity_index] --[[@as evolved.id?]] - if not entity_primary_id or entity_primary_id % 2 ^ 20 ~= entity_index then + local entity_primary_id = __freelist_ids[entity_primary] --[[@as evolved.id?]] + if not entity_primary_id or entity_primary_id % 2 ^ 20 ~= entity_primary then return 0 end end - local entity_chunk = __entity_chunks[entity_index] + local entity_chunk = __entity_chunks[entity_primary] if not entity_chunk then return 0 @@ -6611,7 +6609,7 @@ function __evolved_secondary_count(entity, primary) local primary_index, _, primary_options = __evolved_unpack(primary) - if primary_options >= __PAIR_OPTS then + if primary_options >= __PAIR_OPTIONS then __error_fmt('the pair (%s) cannot be used as a primary fragment', __universal_name(primary)) end @@ -6814,20 +6812,20 @@ function __builder_mt:has(fragment) local maybe_has_pairs = primary_pairs and secondary_pairs - if maybe_has_pairs and fragment >= __PRI_WILDCARD_OPTS * 2 ^ 40 then + if maybe_has_pairs and fragment >= __PRI_WILDCARD_OPTIONS * 2 ^ 40 then ---@cast primary_pairs -? ---@cast secondary_pairs -? local fragment_primary, fragment_secondary, fragment_options = __evolved_unpack(fragment) - if fragment_options == __ANY_WILDCARD_OPTS then + if fragment_options == __ANY_WILDCARD_OPTIONSS then return __lua_next(primary_pairs) ~= nil and __lua_next(secondary_pairs) ~= nil - elseif fragment_options == __PRI_WILDCARD_OPTS then + elseif fragment_options == __PRI_WILDCARD_OPTIONS then local secondary_fragments = secondary_pairs[fragment_secondary] return secondary_fragments ~= nil and secondary_fragments.__item_count > 0 - elseif fragment_options == __SEC_WILDCARD_OPTS then + elseif fragment_options == __SEC_WILDCARD_OPTIONS then local primary_fragments = primary_pairs[fragment_primary] return primary_fragments ~= nil and primary_fragments.__item_count > 0 end @@ -6979,34 +6977,31 @@ end ---@param component evolved.component ---@return evolved.builder builder function __builder_mt:set(fragment, component) - local fragment_primary, fragment_secondary, fragment_options = __evolved_unpack(fragment) + local fragment_primary, fragment_secondary, fragment_options = + __evolved_unpack(fragment) - if fragment_options < __PAIR_OPTS then - local fragment_index = fragment_primary - - if fragment_index == __ANY_INDEX then + if fragment_options < __PAIR_OPTIONS then + if fragment_primary == __ANY_INDEX then __error_fmt('the id (%s) is a wildcard and cannot be set', __universal_name(fragment)) - elseif __freelist_ids[fragment_index] ~= fragment then + elseif __freelist_ids[fragment_primary] ~= fragment then __error_fmt('the id (%s) is not alive and cannot be set', __universal_name(fragment)) end else - local primary_index, secondary_index = fragment_primary, fragment_secondary - - if fragment_options >= __PRI_WILDCARD_OPTS then + if fragment_options >= __PRI_WILDCARD_OPTIONS then __error_fmt('the pair (%s) is a wildcard and cannot be set', __universal_name(fragment)) end - local fragment_primary_id = __freelist_ids[primary_index] --[[@as evolved.id?]] - if not fragment_primary_id or fragment_primary_id % 2 ^ 20 ~= primary_index then + local fragment_primary_id = __freelist_ids[fragment_primary] --[[@as evolved.id?]] + if not fragment_primary_id or fragment_primary_id % 2 ^ 20 ~= fragment_primary then __error_fmt('the pair (%s) has no alive primary id and cannot be set', __universal_name(fragment)) end - local fragment_secondary_id = __freelist_ids[secondary_index] --[[@as evolved.id?]] - if not fragment_secondary_id or fragment_secondary_id % 2 ^ 20 ~= secondary_index then + local fragment_secondary_id = __freelist_ids[fragment_secondary] --[[@as evolved.id?]] + if not fragment_secondary_id or fragment_secondary_id % 2 ^ 20 ~= fragment_secondary then __error_fmt('the pair (%s) has no alive secondary id and cannot be set', __universal_name(fragment)) end @@ -7025,7 +7020,7 @@ function __builder_mt:set(fragment, component) self.__components[fragment] = new_component end - if fragment_options >= __PAIR_OPTS then + if fragment_options >= __PAIR_OPTIONS then local primary_pairs = self.__primary_pairs local secondary_pairs = self.__secondary_pairs @@ -7081,14 +7076,14 @@ function __builder_mt:remove(...) local maybe_has_pairs = primary_pairs and secondary_pairs - if maybe_has_pairs and fragment >= __PRI_WILDCARD_OPTS * 2 ^ 40 then + if maybe_has_pairs and fragment >= __PRI_WILDCARD_OPTIONS * 2 ^ 40 then ---@cast primary_pairs -? ---@cast secondary_pairs -? local fragment_primary, fragment_secondary, fragment_options = __evolved_unpack(fragment) - if fragment_options == __ANY_WILDCARD_OPTS then + if fragment_options == __ANY_WILDCARD_OPTIONSS then for primary_index, primary_fragments in __lua_next, primary_pairs do local primary_fragment_list = primary_fragments.__item_list local primary_fragment_count = primary_fragments.__item_count @@ -7112,7 +7107,7 @@ function __builder_mt:remove(...) secondary_pairs[secondary_index] = nil end - elseif fragment_options == __PRI_WILDCARD_OPTS then + elseif fragment_options == __PRI_WILDCARD_OPTIONS then local secondary_fragments = secondary_pairs[fragment_secondary] local secondary_fragment_list = secondary_fragments and secondary_fragments.__item_list local secondary_fragment_count = secondary_fragments and secondary_fragments.__item_count or 0 @@ -7128,7 +7123,7 @@ function __builder_mt:remove(...) end secondary_pairs[fragment_secondary] = nil - elseif fragment_options == __SEC_WILDCARD_OPTS then + elseif fragment_options == __SEC_WILDCARD_OPTIONS then local primary_fragments = primary_pairs[fragment_primary] local primary_fragment_list = primary_fragments and primary_fragments.__item_list local primary_fragment_count = primary_fragments and primary_fragments.__item_count or 0 From 26bf58614073268effad4bf64c8da515a6e135a7 Mon Sep 17 00:00:00 2001 From: BlackMATov Date: Mon, 1 Sep 2025 01:53:29 +0700 Subject: [PATCH 09/12] primary/secondary functon set doesn't work with pairs as entity now --- develop/testing/pairs_tests.lua | 62 +++------------- evolved.lua | 123 +++++++++++++++++--------------- 2 files changed, 74 insertions(+), 111 deletions(-) diff --git a/develop/testing/pairs_tests.lua b/develop/testing/pairs_tests.lua index 932c0ec..f156fce 100644 --- a/develop/testing/pairs_tests.lua +++ b/develop/testing/pairs_tests.lua @@ -1723,43 +1723,26 @@ do assert(fst(evo.primary(e, s2, 2)) == nil) assert(snd(evo.primary(e, s2, 2)) == nil) - assert(fst(evo.primary(evo.pair(e, f), s1)) == p) - assert(snd(evo.primary(evo.pair(e, f), s1)) == 21) - assert(fst(evo.primary(evo.pair(e, f), s2)) == p) - assert(snd(evo.primary(evo.pair(e, f), s2)) == 42) - assert(fst(evo.primary(evo.pair(e, f), s1, 1)) == p) - assert(snd(evo.primary(evo.pair(e, f), s1, 1)) == 21) - assert(fst(evo.primary(evo.pair(e, f), s2, 1)) == p) - assert(snd(evo.primary(evo.pair(e, f), s2, 1)) == 42) + assert(fst(evo.primary(evo.pair(e, f), s1)) == nil) + assert(fst(evo.primary(evo.pair(e, f), s2)) == nil) + assert(fst(evo.primary(evo.pair(e, f), s1, 1)) == nil) + assert(fst(evo.primary(evo.pair(e, f), s2, 1)) == nil) assert(fst(evo.primary(evo.pair(e, f), s1, 2)) == nil) - assert(snd(evo.primary(evo.pair(e, f), s1, 2)) == nil) assert(fst(evo.primary(evo.pair(e, f), s2, 2)) == nil) - assert(snd(evo.primary(evo.pair(e, f), s2, 2)) == nil) assert(fst(evo.secondary(e, p)) == s1) - assert(snd(evo.secondary(e, p)) == 21) assert(fst(evo.secondary(e, p, 1)) == s1) - assert(snd(evo.secondary(e, p, 1)) == 21) assert(fst(evo.secondary(e, p, 2)) == s2) - assert(snd(evo.secondary(e, p, 2)) == 42) assert(fst(evo.secondary(e, p, 3)) == nil) - assert(snd(evo.secondary(e, p, 3)) == nil) - assert(fst(evo.secondary(evo.pair(e, f), p)) == s1) - assert(snd(evo.secondary(evo.pair(e, f), p)) == 21) - assert(fst(evo.secondary(evo.pair(e, f), p, 1)) == s1) - assert(snd(evo.secondary(evo.pair(e, f), p, 1)) == 21) - assert(fst(evo.secondary(evo.pair(e, f), p, 2)) == s2) - assert(snd(evo.secondary(evo.pair(e, f), p, 2)) == 42) + assert(fst(evo.secondary(evo.pair(e, f), p)) == nil) + assert(fst(evo.secondary(evo.pair(e, f), p, 1)) == nil) + assert(fst(evo.secondary(evo.pair(e, f), p, 2)) == nil) assert(fst(evo.secondary(evo.pair(e, f), p, 3)) == nil) - assert(snd(evo.secondary(evo.pair(e, f), p, 3)) == nil) assert(fst(evo.primary(evo.pair(f, e), s1)) == nil) - assert(snd(evo.primary(evo.pair(f, e), s1)) == nil) assert(fst(evo.primary(evo.pair(f, e), s2)) == nil) - assert(snd(evo.primary(evo.pair(f, e), s2)) == nil) assert(fst(evo.secondary(evo.pair(f, e), p)) == nil) - assert(snd(evo.secondary(evo.pair(f, e), p)) == nil) end do @@ -1778,9 +1761,9 @@ do assert(evo.secondary_count(e, s2) == 0) assert(evo.primary_count(evo.pair(e, f), p) == 0) - assert(evo.primary_count(evo.pair(e, f), s1) == 1) - assert(evo.primary_count(evo.pair(e, f), s2) == 1) - assert(evo.secondary_count(evo.pair(e, f), p) == 2) + assert(evo.primary_count(evo.pair(e, f), s1) == 0) + assert(evo.primary_count(evo.pair(e, f), s2) == 0) + assert(evo.secondary_count(evo.pair(e, f), p) == 0) assert(evo.secondary_count(evo.pair(e, f), s1) == 0) assert(evo.secondary_count(evo.pair(e, f), s2) == 0) @@ -1820,8 +1803,6 @@ do iter, state = evo.primaries(evo.pair(e, f), s1) fragment, component = iter(state) - assert(fragment == p and component == 21) - fragment, component = iter(state) assert(fragment == nil and component == nil) end @@ -1834,8 +1815,6 @@ do iter, state = evo.primaries(evo.pair(e, f), s2) fragment, component = iter(state) - assert(fragment == p and component == 42) - fragment, component = iter(state) assert(fragment == nil and component == nil) end end @@ -1869,10 +1848,6 @@ do iter, state = evo.secondaries(evo.pair(e, f), p) fragment, component = iter(state) - assert(fragment == s1 and component == 21) - fragment, component = iter(state) - assert(fragment == s2 and component == 42) - fragment, component = iter(state) assert(fragment == nil and component == nil) end end @@ -1936,26 +1911,9 @@ do assert(not evo.has(e, evo.pair(p, s1)) and evo.get(e, evo.pair(p, s1)) == nil) assert(not evo.has(e, evo.pair(p, s2)) and evo.get(e, evo.pair(p, s2)) == nil) end - - do - local p, s = evo.id(2) - - evo.destroy(s) - - evo.debug_mode(false) - - local e = evo.spawn { - [evo.pair(p, s)] = 21, - } - - evo.debug_mode(true) - - assert(evo.has(e, evo.pair(p, s)) and evo.get(e, evo.pair(p, s)) == 21) - end end -- TODO --- builder:has/has_all/has_any should work with wildcards / remove too? -- should we provide wildcard support for get operations? -- prevent setting pairs with dead secondary fragments -- process evo.ANY as single wildcard diff --git a/evolved.lua b/evolved.lua index 61789e3..3a658cb 100644 --- a/evolved.lua +++ b/evolved.lua @@ -1373,8 +1373,34 @@ local __update_major_chunks_trace ---@return evolved.chunk ---@nodiscard function __new_chunk(chunk_parent, chunk_fragment) - if chunk_fragment >= __PRI_WILDCARD_OPTIONS * 2 ^ 40 then - __error_fmt('chunk cannot contain wildcard fragments') + local chunk_fragment_primary, chunk_fragment_secondary, chunk_fragment_options = + __evolved_unpack(chunk_fragment) + + if chunk_fragment_options < __PAIR_OPTIONS then + if chunk_fragment_primary == __ANY_INDEX then + __error_fmt('the id (%s) is a wildcard and cannot be used for a new chunk', + __universal_name(chunk_fragment)) + elseif __freelist_ids[chunk_fragment_primary] ~= chunk_fragment then + __error_fmt('the id (%s) is not alive and cannot be used for a new chunk', + __universal_name(chunk_fragment)) + end + else + if chunk_fragment_options >= __PRI_WILDCARD_OPTIONS then + __error_fmt('the pair (%s) is a wildcard and cannot be used for a new chunk', + __universal_name(chunk_fragment)) + end + + local fragment_primary_id = __freelist_ids[chunk_fragment_primary] --[[@as evolved.id?]] + if not fragment_primary_id or fragment_primary_id % 2 ^ 20 ~= chunk_fragment_primary then + __error_fmt('the pair (%s) has no alive primary id and cannot be used for a new chunk', + __universal_name(chunk_fragment)) + end + + local fragment_secondary_id = __freelist_ids[chunk_fragment_secondary] --[[@as evolved.id?]] + if not fragment_secondary_id or fragment_secondary_id % 2 ^ 20 ~= chunk_fragment_secondary then + __error_fmt('the pair (%s) has no alive secondary id and cannot be used for a new chunk', + __universal_name(chunk_fragment)) + end end local chunk_fragment_set = {} ---@type table @@ -1427,9 +1453,6 @@ function __new_chunk(chunk_parent, chunk_fragment) chunk_pair_count = chunk_pair_count + 1 chunk_pair_list[chunk_pair_count] = chunk_fragment - local chunk_fragment_primary, chunk_fragment_secondary = - __evolved_unpack(chunk_fragment) - local chunk_primary_fragments = chunk_primary_pairs[chunk_fragment_primary] local chunk_secondary_fragments = chunk_secondary_pairs[chunk_fragment_secondary] @@ -6352,15 +6375,12 @@ function __evolved_primary(entity, secondary, index) local entity_primary, _, entity_options = __evolved_unpack(entity) - if entity_options < __PAIR_OPTIONS then - if __freelist_ids[entity_primary] ~= entity then - return - end - else - local entity_primary_id = __freelist_ids[entity_primary] --[[@as evolved.id?]] - if not entity_primary_id or entity_primary_id % 2 ^ 20 ~= entity_primary then - return - end + if entity_options >= __PAIR_OPTIONS then + return + end + + if __freelist_ids[entity_primary] ~= entity then + return end local entity_chunk = __entity_chunks[entity_primary] @@ -6405,15 +6425,12 @@ function __evolved_secondary(entity, primary, index) local entity_primary, _, entity_options = __evolved_unpack(entity) - if entity_options < __PAIR_OPTIONS then - if __freelist_ids[entity_primary] ~= entity then - return - end - else - local entity_primary_id = __freelist_ids[entity_primary] --[[@as evolved.id?]] - if not entity_primary_id or entity_primary_id % 2 ^ 20 ~= entity_primary then - return - end + if entity_options >= __PAIR_OPTIONS then + return + end + + if __freelist_ids[entity_primary] ~= entity then + return end local entity_chunk = __entity_chunks[entity_primary] @@ -6455,15 +6472,12 @@ end function __evolved_primaries(entity, secondary) local entity_primary, _, entity_options = __evolved_unpack(entity) - if entity_options < __PAIR_OPTIONS then - if __freelist_ids[entity_primary] ~= entity then - return __iterator_fns.__primaries_iterator - end - else - local entity_primary_id = __freelist_ids[entity_primary] --[[@as evolved.id?]] - if not entity_primary_id or entity_primary_id % 2 ^ 20 ~= entity_primary then - return __iterator_fns.__primaries_iterator - end + if entity_options >= __PAIR_OPTIONS then + return __iterator_fns.__primaries_iterator + end + + if __freelist_ids[entity_primary] ~= entity then + return __iterator_fns.__primaries_iterator end local entity_chunk = __entity_chunks[entity_primary] @@ -6505,15 +6519,12 @@ end function __evolved_secondaries(entity, primary) local entity_primary, _, entity_options = __evolved_unpack(entity) - if entity_options < __PAIR_OPTIONS then - if __freelist_ids[entity_primary] ~= entity then - return __iterator_fns.__secondaries_iterator - end - else - local entity_primary_id = __freelist_ids[entity_primary] --[[@as evolved.id?]] - if not entity_primary_id or entity_primary_id % 2 ^ 20 ~= entity_primary then - return __iterator_fns.__secondaries_iterator - end + if entity_options >= __PAIR_OPTIONS then + return __iterator_fns.__secondaries_iterator + end + + if __freelist_ids[entity_primary] ~= entity then + return __iterator_fns.__secondaries_iterator end local entity_chunk = __entity_chunks[entity_primary] @@ -6554,15 +6565,12 @@ end function __evolved_primary_count(entity, secondary) local entity_primary, _, entity_options = __evolved_unpack(entity) - if entity_options < __PAIR_OPTIONS then - if __freelist_ids[entity_primary] ~= entity then - return 0 - end - else - local entity_primary_id = __freelist_ids[entity_primary] --[[@as evolved.id?]] - if not entity_primary_id or entity_primary_id % 2 ^ 20 ~= entity_primary then - return 0 - end + if entity_options >= __PAIR_OPTIONS then + return 0 + end + + if __freelist_ids[entity_primary] ~= entity then + return 0 end local entity_chunk = __entity_chunks[entity_primary] @@ -6590,15 +6598,12 @@ end function __evolved_secondary_count(entity, primary) local entity_primary, _, entity_options = __evolved_unpack(entity) - if entity_options < __PAIR_OPTIONS then - if __freelist_ids[entity_primary] ~= entity then - return 0 - end - else - local entity_primary_id = __freelist_ids[entity_primary] --[[@as evolved.id?]] - if not entity_primary_id or entity_primary_id % 2 ^ 20 ~= entity_primary then - return 0 - end + if entity_options >= __PAIR_OPTIONS then + return 0 + end + + if __freelist_ids[entity_primary] ~= entity then + return 0 end local entity_chunk = __entity_chunks[entity_primary] From 0c016f1b67e2f433741ac69ca3501a057f422ed8 Mon Sep 17 00:00:00 2001 From: BlackMATov Date: Mon, 1 Sep 2025 01:59:00 +0700 Subject: [PATCH 10/12] return xxx_set debug mode --- evolved.lua | 142 +++++++++++++++++++++++++++------------------------- 1 file changed, 74 insertions(+), 68 deletions(-) diff --git a/evolved.lua b/evolved.lua index 3a658cb..566867c 100644 --- a/evolved.lua +++ b/evolved.lua @@ -5213,30 +5213,32 @@ function __evolved_set(entity, fragment, component) local fragment_primary, fragment_secondary, fragment_options = __evolved_unpack(fragment) - if fragment_options < __PAIR_OPTIONS then - if fragment_primary == __ANY_INDEX then - __error_fmt('the id (%s) is a wildcard and cannot be set', - __universal_name(fragment)) - elseif __freelist_ids[fragment_primary] ~= fragment then - __error_fmt('the id (%s) is not alive and cannot be set', - __universal_name(fragment)) - end - else - if fragment_options >= __PRI_WILDCARD_OPTIONS then - __error_fmt('the pair (%s) is a wildcard and cannot be set', - __universal_name(fragment)) - end + if __debug_mode then + if fragment_options < __PAIR_OPTIONS then + if fragment_primary == __ANY_INDEX then + __error_fmt('the id (%s) is a wildcard and cannot be set', + __universal_name(fragment)) + elseif __freelist_ids[fragment_primary] ~= fragment then + __error_fmt('the id (%s) is not alive and cannot be set', + __universal_name(fragment)) + end + else + if fragment_options >= __PRI_WILDCARD_OPTIONS then + __error_fmt('the pair (%s) is a wildcard and cannot be set', + __universal_name(fragment)) + end - local fragment_primary_id = __freelist_ids[fragment_primary] --[[@as evolved.id?]] - if not fragment_primary_id or fragment_primary_id % 2 ^ 20 ~= fragment_primary then - __error_fmt('the pair (%s) has no alive primary id and cannot be set', - __universal_name(fragment)) - end + local fragment_primary_id = __freelist_ids[fragment_primary] --[[@as evolved.id?]] + if not fragment_primary_id or fragment_primary_id % 2 ^ 20 ~= fragment_primary then + __error_fmt('the pair (%s) has no alive primary id and cannot be set', + __universal_name(fragment)) + end - local fragment_secondary_id = __freelist_ids[fragment_secondary] --[[@as evolved.id?]] - if not fragment_secondary_id or fragment_secondary_id % 2 ^ 20 ~= fragment_secondary then - __error_fmt('the pair (%s) has no alive secondary id and cannot be set', - __universal_name(fragment)) + local fragment_secondary_id = __freelist_ids[fragment_secondary] --[[@as evolved.id?]] + if not fragment_secondary_id or fragment_secondary_id % 2 ^ 20 ~= fragment_secondary then + __error_fmt('the pair (%s) has no alive secondary id and cannot be set', + __universal_name(fragment)) + end end end @@ -5726,33 +5728,35 @@ function __evolved_batch_set(query, fragment, component) __universal_name(query)) end - local fragment_primary, fragment_secondary, fragment_options = - __evolved_unpack(fragment) + if __debug_mode then + local fragment_primary, fragment_secondary, fragment_options = + __evolved_unpack(fragment) - if fragment_options < __PAIR_OPTIONS then - if fragment_primary == __ANY_INDEX then - __error_fmt('the id (%s) is a wildcard and cannot be set', - __universal_name(fragment)) - elseif __freelist_ids[fragment_primary] ~= fragment then - __error_fmt('the id (%s) is not alive and cannot be set', - __universal_name(fragment)) - end - else - if fragment_options >= __PRI_WILDCARD_OPTIONS then - __error_fmt('the pair (%s) is a wildcard and cannot be set', - __universal_name(fragment)) - end + if fragment_options < __PAIR_OPTIONS then + if fragment_primary == __ANY_INDEX then + __error_fmt('the id (%s) is a wildcard and cannot be set', + __universal_name(fragment)) + elseif __freelist_ids[fragment_primary] ~= fragment then + __error_fmt('the id (%s) is not alive and cannot be set', + __universal_name(fragment)) + end + else + if fragment_options >= __PRI_WILDCARD_OPTIONS then + __error_fmt('the pair (%s) is a wildcard and cannot be set', + __universal_name(fragment)) + end - local fragment_primary_id = __freelist_ids[fragment_primary] --[[@as evolved.id?]] - if not fragment_primary_id or fragment_primary_id % 2 ^ 20 ~= fragment_primary then - __error_fmt('the pair (%s) has no alive primary id and cannot be set', - __universal_name(fragment)) - end + local fragment_primary_id = __freelist_ids[fragment_primary] --[[@as evolved.id?]] + if not fragment_primary_id or fragment_primary_id % 2 ^ 20 ~= fragment_primary then + __error_fmt('the pair (%s) has no alive primary id and cannot be set', + __universal_name(fragment)) + end - local fragment_secondary_id = __freelist_ids[fragment_secondary] --[[@as evolved.id?]] - if not fragment_secondary_id or fragment_secondary_id % 2 ^ 20 ~= fragment_secondary then - __error_fmt('the pair (%s) has no alive secondary id and cannot be set', - __universal_name(fragment)) + local fragment_secondary_id = __freelist_ids[fragment_secondary] --[[@as evolved.id?]] + if not fragment_secondary_id or fragment_secondary_id % 2 ^ 20 ~= fragment_secondary then + __error_fmt('the pair (%s) has no alive secondary id and cannot be set', + __universal_name(fragment)) + end end end @@ -6985,30 +6989,32 @@ function __builder_mt:set(fragment, component) local fragment_primary, fragment_secondary, fragment_options = __evolved_unpack(fragment) - if fragment_options < __PAIR_OPTIONS then - if fragment_primary == __ANY_INDEX then - __error_fmt('the id (%s) is a wildcard and cannot be set', - __universal_name(fragment)) - elseif __freelist_ids[fragment_primary] ~= fragment then - __error_fmt('the id (%s) is not alive and cannot be set', - __universal_name(fragment)) - end - else - if fragment_options >= __PRI_WILDCARD_OPTIONS then - __error_fmt('the pair (%s) is a wildcard and cannot be set', - __universal_name(fragment)) - end + if __debug_mode then + if fragment_options < __PAIR_OPTIONS then + if fragment_primary == __ANY_INDEX then + __error_fmt('the id (%s) is a wildcard and cannot be set', + __universal_name(fragment)) + elseif __freelist_ids[fragment_primary] ~= fragment then + __error_fmt('the id (%s) is not alive and cannot be set', + __universal_name(fragment)) + end + else + if fragment_options >= __PRI_WILDCARD_OPTIONS then + __error_fmt('the pair (%s) is a wildcard and cannot be set', + __universal_name(fragment)) + end - local fragment_primary_id = __freelist_ids[fragment_primary] --[[@as evolved.id?]] - if not fragment_primary_id or fragment_primary_id % 2 ^ 20 ~= fragment_primary then - __error_fmt('the pair (%s) has no alive primary id and cannot be set', - __universal_name(fragment)) - end + local fragment_primary_id = __freelist_ids[fragment_primary] --[[@as evolved.id?]] + if not fragment_primary_id or fragment_primary_id % 2 ^ 20 ~= fragment_primary then + __error_fmt('the pair (%s) has no alive primary id and cannot be set', + __universal_name(fragment)) + end - local fragment_secondary_id = __freelist_ids[fragment_secondary] --[[@as evolved.id?]] - if not fragment_secondary_id or fragment_secondary_id % 2 ^ 20 ~= fragment_secondary then - __error_fmt('the pair (%s) has no alive secondary id and cannot be set', - __universal_name(fragment)) + local fragment_secondary_id = __freelist_ids[fragment_secondary] --[[@as evolved.id?]] + if not fragment_secondary_id or fragment_secondary_id % 2 ^ 20 ~= fragment_secondary then + __error_fmt('the pair (%s) has no alive secondary id and cannot be set', + __universal_name(fragment)) + end end end From 8f61a14db62aa0bc339c40da6b12060f075eed76 Mon Sep 17 00:00:00 2001 From: BlackMATov Date: Mon, 1 Sep 2025 02:02:10 +0700 Subject: [PATCH 11/12] update pairs todos --- develop/testing/pairs_tests.lua | 5 ++--- evolved.lua | 20 ++++++++++---------- 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/develop/testing/pairs_tests.lua b/develop/testing/pairs_tests.lua index f156fce..86e4d22 100644 --- a/develop/testing/pairs_tests.lua +++ b/develop/testing/pairs_tests.lua @@ -1914,8 +1914,7 @@ do end -- TODO --- should we provide wildcard support for get operations? --- prevent setting pairs with dead secondary fragments -- process evo.ANY as single wildcard -- should we provide an evolved.pair type? --- how should the destroy function handle pairs? +-- we should not add any wildcards to chunk `with` edges, lol +-- edges `without` to nil don't work because they are cannot be found diff --git a/evolved.lua b/evolved.lua index 566867c..9fc3ec7 100644 --- a/evolved.lua +++ b/evolved.lua @@ -132,10 +132,10 @@ local evolved = { | ANY WILDCARD | RSVD | 11 | 1 | ANY index | ANY index | \------------------------------------------------------------------]=] -local __PAIR_OPTIONS = 1 -- 0b001 -local __PRI_WILDCARD_OPTIONS = 3 -- 0b011 -local __SEC_WILDCARD_OPTIONS = 5 -- 0b101 -local __ANY_WILDCARD_OPTIONSS = 7 -- 0b111 +local __PAIR_OPTIONS = 1 -- 0b001 +local __PRI_WILDCARD_OPTIONS = 3 -- 0b011 +local __SEC_WILDCARD_OPTIONS = 5 -- 0b101 +local __ANY_WILDCARD_OPTIONS = 7 -- 0b111 --- --- @@ -826,7 +826,7 @@ local __ANY_INDEX = __ANY % 2 ^ 20 --[[@as integer]] local __ANY_WILDCARD = __ANY_INDEX + __ANY_INDEX * 2 ^ 20 - + __ANY_WILDCARD_OPTIONSS * 2 ^ 40 --[[@as evolved.pair]] + + __ANY_WILDCARD_OPTIONS * 2 ^ 40 --[[@as evolved.pair]] --- --- @@ -2018,7 +2018,7 @@ function __chunk_without_fragment(chunk, fragment) local fragment_primary, fragment_secondary, fragment_options = __evolved_unpack(fragment) - if fragment_options == __ANY_WILDCARD_OPTIONSS then + if fragment_options == __ANY_WILDCARD_OPTIONS then while chunk and chunk.__has_pair_major do chunk = chunk.__parent end @@ -2255,7 +2255,7 @@ function __chunk_has_fragment(chunk, fragment) local fragment_primary, fragment_secondary, fragment_options = __evolved_unpack(fragment) - if fragment_options == __ANY_WILDCARD_OPTIONSS then + if fragment_options == __ANY_WILDCARD_OPTIONS then return true elseif fragment_options == __PRI_WILDCARD_OPTIONS then local secondary_fragments = chunk.__secondary_pairs[fragment_secondary] @@ -6314,7 +6314,7 @@ function __evolved_pair(primary, secondary) local pair_options = __PAIR_OPTIONS if primary_index == __ANY_INDEX and secondary_index == __ANY_INDEX then - pair_options = __ANY_WILDCARD_OPTIONSS + pair_options = __ANY_WILDCARD_OPTIONS elseif primary_index == __ANY_INDEX then pair_options = __PRI_WILDCARD_OPTIONS elseif secondary_index == __ANY_INDEX then @@ -6828,7 +6828,7 @@ function __builder_mt:has(fragment) local fragment_primary, fragment_secondary, fragment_options = __evolved_unpack(fragment) - if fragment_options == __ANY_WILDCARD_OPTIONSS then + if fragment_options == __ANY_WILDCARD_OPTIONS then return __lua_next(primary_pairs) ~= nil and __lua_next(secondary_pairs) ~= nil elseif fragment_options == __PRI_WILDCARD_OPTIONS then @@ -7094,7 +7094,7 @@ function __builder_mt:remove(...) local fragment_primary, fragment_secondary, fragment_options = __evolved_unpack(fragment) - if fragment_options == __ANY_WILDCARD_OPTIONSS then + if fragment_options == __ANY_WILDCARD_OPTIONS then for primary_index, primary_fragments in __lua_next, primary_pairs do local primary_fragment_list = primary_fragments.__item_list local primary_fragment_count = primary_fragments.__item_count From fb6d13ca74735e7ef6ef5ab66337fb66b6054d5f Mon Sep 17 00:00:00 2001 From: BlackMATov Date: Mon, 1 Sep 2025 15:32:32 +0700 Subject: [PATCH 12/12] temp remove pairs to merge other changes to dev --- README.md | 143 +- develop/all.lua | 4 - develop/fuzzing/pack_unpack_fuzz.lua | 3 +- develop/fuzzing/wildcard_fuzz.lua | 275 ---- develop/samples/relations.lua | 79 -- develop/testing/name_tests.lua | 6 +- develop/testing/pairs_tests.lua | 1920 ------------------------- evolved.lua | 1962 +++----------------------- 8 files changed, 215 insertions(+), 4177 deletions(-) delete mode 100644 develop/fuzzing/wildcard_fuzz.lua delete mode 100644 develop/samples/relations.lua delete mode 100644 develop/testing/pairs_tests.lua diff --git a/README.md b/README.md index 164000b..07521f4 100644 --- a/README.md +++ b/README.md @@ -53,8 +53,7 @@ - [Cheat Sheet](#cheat-sheet) - [Aliases](#aliases) - [Predefs](#predefs) - - [Core Functions](#core-functions) - - [Relation Functions](#relation-functions) + - [Functions](#functions) - [Classes](#classes) - [Chunk](#chunk) - [Builder](#builder) @@ -157,7 +156,6 @@ function evolved.pack(index, version) end ---@param id evolved.id ---@return integer primary ---@return integer secondary ----@return integer options ---@nodiscard function evolved.unpack(id) end ``` @@ -1026,7 +1024,6 @@ assert(not evolved.alive(entity)) ``` id :: implementation-specific -pair :: id entity :: id fragment :: id @@ -1050,20 +1047,14 @@ remove_hook :: {entity, fragment, component} each_state :: implementation-specific execute_state :: implementation-specific -primaries_state :: implementation-specific -secondaries_state :: implementation-specific each_iterator :: {each_state? -> fragment?, component?} execute_iterator :: {execute_state? -> chunk?, entity[]?, integer?} -primaries_iterator :: {primaries_state? -> fragment?, component?} -secondaries_iterator :: {secondaries_state? -> fragment?, component?} ``` ### Predefs ``` -ANY :: fragment - TAG :: fragment NAME :: fragment @@ -1099,7 +1090,7 @@ DESTRUCTION_POLICY_DESTROY_ENTITY :: id DESTRUCTION_POLICY_REMOVE_FRAGMENT :: id ``` -### Core Functions +### Functions ``` id :: integer? -> id... @@ -1147,25 +1138,6 @@ debug_mode :: boolean -> () collect_garbage :: () ``` -### Relation Functions - -``` -pair :: id, id -> pair -unpair :: pair -> id, id - -is_pair :: id -> boolean -is_wildcard :: id -> boolean - -primary :: entity, fragment, integer? -> fragment?, component? -secondary :: entity, fragment, integer? -> fragment?, component? - -primaries :: entity, fragment -> {primaries_state? -> fragment?, component?}, primaries_state? -secondaries :: entity, fragment -> {secondaries_state? -> fragment?, component?}, secondaries_state? - -primary_count :: entity, fragment -> integer -secondary_count :: entity, fragment -> integer -``` - ### Classes #### Chunk @@ -1260,8 +1232,6 @@ builder_mt:destruction_policy :: id -> builder ## Predefs -### `evolved.ANY` - ### `evolved.TAG` ### `evolved.NAME` @@ -1310,7 +1280,7 @@ builder_mt:destruction_policy :: id -> builder ### `evolved.DESTRUCTION_POLICY_REMOVE_FRAGMENT` -## Core Functions +## Functions ### `evolved.id` @@ -1346,7 +1316,6 @@ function evolved.pack(index, version) end ---@param id evolved.id ---@return integer primary ---@return integer secondary ----@return integer options ---@nodiscard function evolved.unpack(id) end ``` @@ -1578,112 +1547,6 @@ function evolved.debug_mode(yesno) end function evolved.collect_garbage() end ``` -## Relation Functions - -### `evolved.pair` - -```lua ----@param primary evolved.id ----@param secondary evolved.id ----@return evolved.pair pair ----@nodiscard -function evolved.pair(primary, secondary) end -``` - -### `evolved.unpair` - -```lua ----@param pair evolved.pair ----@return evolved.id primary ----@return evolved.id secondary ----@nodiscard -function evolved.unpair(pair) end -``` - -### `evolved.is_pair` - -```lua ----@param id evolved.id ----@return boolean ----@nodiscard -function evolved.is_pair(id) end -``` - -### `evolved.is_wildcard` - -```lua ----@param id evolved.id ----@return boolean ----@nodiscard -function evolved.is_wildcard(id) end -``` - -### `evolved.primary` - -```lua ----@param entity evolved.entity ----@param secondary evolved.fragment ----@param index? integer ----@return evolved.fragment? primary ----@return evolved.component? component ----@nodiscard -function evolved.primary(entity, secondary, index) end -``` - -### `evolved.secondary` - -```lua ----@param entity evolved.entity ----@param primary evolved.fragment ----@param index? integer ----@return evolved.fragment? secondary ----@return evolved.component? component ----@nodiscard -function evolved.secondary(entity, primary, index) end -``` - -### `evolved.primaries` - -```lua ----@param entity evolved.entity ----@param secondary evolved.fragment ----@return evolved.primaries_iterator iterator ----@return evolved.primaries_state? iterator_state ----@nodiscard -function evolved.primaries(entity, secondary) end -``` - -### `evolved.secondaries` - -```lua ----@param entity evolved.entity ----@param primary evolved.fragment ----@return evolved.secondaries_iterator iterator ----@return evolved.secondaries_state? iterator_state ----@nodiscard -function evolved.secondaries(entity, primary) end -``` - -### `evolved.primary_count` - -```lua ----@param entity evolved.entity ----@param secondary evolved.fragment ----@return integer ----@nodiscard -function evolved.primary_count(entity, secondary) end -``` - -### `evolved.secondary_count` - -```lua ----@param entity evolved.entity ----@param primary evolved.fragment ----@return integer ----@nodiscard -function evolved.secondary_count(entity, primary) end -``` - ## Classes ### Chunk diff --git a/develop/all.lua b/develop/all.lua index b5d6025..bd34bf9 100644 --- a/develop/all.lua +++ b/develop/all.lua @@ -1,8 +1,6 @@ -require 'develop.samples.relations' require 'develop.samples.systems' require 'develop.testing.name_tests' -require 'develop.testing.pairs_tests' require 'develop.testing.requires_fragment_tests' require 'develop.testing.system_as_query_tests' @@ -25,5 +23,3 @@ print '----------------------------------------' basics.describe_fuzz 'develop.fuzzing.requires_fuzz' print '----------------------------------------' basics.describe_fuzz 'develop.fuzzing.unique_fuzz' -print '----------------------------------------' -basics.describe_fuzz 'develop.fuzzing.wildcard_fuzz' diff --git a/develop/fuzzing/pack_unpack_fuzz.lua b/develop/fuzzing/pack_unpack_fuzz.lua index b1dce49..2693913 100644 --- a/develop/fuzzing/pack_unpack_fuzz.lua +++ b/develop/fuzzing/pack_unpack_fuzz.lua @@ -13,9 +13,8 @@ for _ = 1, 1000 do local initial_secondary = math.random(1, 2 ^ 20 - 1) local packed_id = evo.pack(initial_primary, initial_secondary) - local unpacked_primary, unpacked_secondary, unpacked_options = evo.unpack(packed_id) + local unpacked_primary, unpacked_secondary = evo.unpack(packed_id) assert(initial_primary == unpacked_primary) assert(initial_secondary == unpacked_secondary) - assert(0 == unpacked_options) end diff --git a/develop/fuzzing/wildcard_fuzz.lua b/develop/fuzzing/wildcard_fuzz.lua deleted file mode 100644 index 9a64fc6..0000000 --- a/develop/fuzzing/wildcard_fuzz.lua +++ /dev/null @@ -1,275 +0,0 @@ -local evo = require 'evolved' - -evo.debug_mode(true) - ---- ---- ---- ---- ---- - -local __table_unpack = (function() - ---@diagnostic disable-next-line: deprecated - return table.unpack or unpack -end)() - ---- ---- ---- ---- ---- - -local all_entity_list = {} ---@type evolved.entity[] -local all_fragment_list = {} ---@type evolved.fragment[] - -for i = 1, math.random(1, 5) do - local fragment_builder = evo.builder() - - if math.random(1, 3) == 1 then - fragment_builder:explicit() - end - - if math.random(1, 3) == 1 then - if math.random(1, 2) == 1 then - fragment_builder:destruction_policy(evo.DESTRUCTION_POLICY_DESTROY_ENTITY) - else - fragment_builder:destruction_policy(evo.DESTRUCTION_POLICY_REMOVE_FRAGMENT) - end - end - - all_fragment_list[i] = fragment_builder:spawn() -end - -for i = 1, math.random(50, 100) do - local entity_builder = evo.builder() - - for _ = 0, math.random(0, #all_fragment_list) do - if math.random(1, 2) == 1 then - local fragment = all_fragment_list[math.random(1, #all_fragment_list)] - entity_builder:set(fragment) - else - local primary = all_fragment_list[math.random(1, #all_fragment_list)] - local secondary = all_fragment_list[math.random(1, #all_fragment_list)] - entity_builder:set(evo.pair(primary, secondary)) - end - end - - all_entity_list[i] = entity_builder:spawn() -end - ---- ---- ---- ---- ---- - -for _ = 1, math.random(1, 100) do - local query_builder = evo.builder() - - local query_include_set = {} ---@type table - local query_include_list = {} ---@type evolved.entity[] - local query_include_count = 0 ---@type integer - - local query_exclude_set = {} ---@type table - local query_exclude_list = {} ---@type evolved.entity[] - local query_exclude_count = 0 ---@type integer - - for _ = 1, math.random(0, 2) do - if math.random(1, 2) == 1 then - local fragment = all_fragment_list[math.random(1, #all_fragment_list)] - - query_builder:include(fragment) - - if not query_include_set[fragment] then - query_include_count = query_include_count + 1 - query_include_set[fragment] = query_include_count - query_include_list[query_include_count] = fragment - end - else - local primary = all_fragment_list[math.random(1, #all_fragment_list)] - local secondary = all_fragment_list[math.random(1, #all_fragment_list)] - - if math.random(1, 3) == 1 then - primary = evo.ANY - end - - if math.random(1, 3) == 1 then - secondary = evo.ANY - end - - local pair = evo.pair(primary, secondary) - - query_builder:include(pair) - - if not query_include_set[pair] then - query_include_count = query_include_count + 1 - query_include_set[pair] = query_include_count - query_include_list[query_include_count] = pair - end - end - end - - for _ = 1, math.random(0, 2) do - if math.random(1, 2) == 1 then - local fragment = all_fragment_list[math.random(1, #all_fragment_list)] - - query_builder:exclude(fragment) - - if not query_exclude_set[fragment] then - query_exclude_count = query_exclude_count + 1 - query_exclude_set[fragment] = query_exclude_count - query_exclude_list[query_exclude_count] = fragment - end - else - local primary = all_fragment_list[math.random(1, #all_fragment_list)] - local secondary = all_fragment_list[math.random(1, #all_fragment_list)] - - if math.random(1, 3) == 1 then - primary = evo.ANY - end - - if math.random(1, 3) == 1 then - secondary = evo.ANY - end - - local pair = evo.pair(primary, secondary) - - query_builder:exclude(pair) - - if not query_exclude_set[pair] then - query_exclude_count = query_exclude_count + 1 - query_exclude_set[pair] = query_exclude_count - query_exclude_list[query_exclude_count] = pair - end - end - end - - local query_entity_set = {} ---@type table - local query_entity_count = 0 ---@type integer - - do - local query = query_builder:spawn() - - for chunk, entity_list, entity_count in evo.execute(query) do - if not chunk:has(evo.INTERNAL) then - for i = 1, entity_count do - local entity = entity_list[i] - assert(not query_entity_set[entity]) - query_entity_count = query_entity_count + 1 - query_entity_set[entity] = query_entity_count - end - end - end - - if query_entity_set[query] then - query_entity_set[query] = nil - query_entity_count = query_entity_count - 1 - end - - evo.destroy(query) - end - - do - local expected_entity_count = 0 - - for _, entity in ipairs(all_entity_list) do - local is_entity_expected = - not evo.empty(entity) and - evo.has_all(entity, __table_unpack(query_include_list)) and - not evo.has_any(entity, __table_unpack(query_exclude_list)) - - for fragment in evo.each(entity) do - local is_fragment_explicit = false - - if not is_fragment_explicit and evo.is_pair(fragment) then - is_fragment_explicit = evo.has(evo.unpair(fragment), evo.EXPLICIT) - end - - if not is_fragment_explicit and not evo.is_pair(fragment) then - is_fragment_explicit = evo.has(fragment, evo.EXPLICIT) - end - - if is_fragment_explicit then - local is_fragment_included = false - - if not is_fragment_included then - is_fragment_included = query_include_set[fragment] ~= nil - end - - if not is_fragment_included and evo.is_pair(fragment) then - local fragment_primary = evo.unpair(fragment) - is_fragment_included = query_include_set[evo.pair(fragment_primary, evo.ANY)] ~= nil - end - - if not is_fragment_included and not evo.is_pair(fragment) then - is_fragment_included = query_include_set[evo.pair(fragment, evo.ANY)] ~= nil - end - - if not is_fragment_included then - is_entity_expected = false - break - end - end - end - - if is_entity_expected then - assert(query_entity_set[entity]) - expected_entity_count = expected_entity_count + 1 - else - assert(not query_entity_set[entity]) - end - end - - for _, entity in ipairs(all_fragment_list) do - local is_entity_expected = - not evo.empty(entity) and - evo.has_all(entity, __table_unpack(query_include_list)) and - not evo.has_any(entity, __table_unpack(query_exclude_list)) - - for fragment in evo.each(entity) do - if evo.has(fragment, evo.EXPLICIT) then - is_entity_expected = is_entity_expected and - (query_include_set[fragment] ~= nil) or - (evo.is_pair(fragment) and query_include_set[evo.pair(fragment, evo.ANY)] ~= nil) - end - end - - if is_entity_expected then - assert(query_entity_set[entity]) - expected_entity_count = expected_entity_count + 1 - else - assert(not query_entity_set[entity]) - end - end - - assert(query_entity_count == expected_entity_count) - end -end - ---- ---- ---- ---- ---- - -if math.random(1, 2) == 1 then - evo.collect_garbage() -end - -if math.random(1, 2) == 1 then - evo.destroy(__table_unpack(all_entity_list)) - if math.random(1, 2) == 1 then - evo.collect_garbage() - end - evo.destroy(__table_unpack(all_fragment_list)) -else - evo.destroy(__table_unpack(all_fragment_list)) - if math.random(1, 2) == 1 then - evo.collect_garbage() - end - evo.destroy(__table_unpack(all_entity_list)) -end - -if math.random(1, 2) == 1 then - evo.collect_garbage() -end diff --git a/develop/samples/relations.lua b/develop/samples/relations.lua deleted file mode 100644 index 6311fbe..0000000 --- a/develop/samples/relations.lua +++ /dev/null @@ -1,79 +0,0 @@ ----@diagnostic disable: unused-local - -local evo = require 'evolved' - -evo.debug_mode(true) - -local fragments = { - planet = evo.builder() - :name('planet') - :tag() - :spawn(), - spaceship = evo.builder() - :name('spaceship') - :tag() - :spawn(), -} - -local relations = { - docked_to = evo.builder() - :name('docked_to') - :tag() - :spawn(), -} - -local planets = { - mars = evo.builder() - :name('Mars') - :set(fragments.planet) - :spawn(), - venus = evo.builder() - :name('Venus') - :set(fragments.planet) - :spawn(), -} - -local spaceships = { - falcon = evo.builder() - :name('Millennium Falcon') - :set(fragments.spaceship) - :set(evo.pair(relations.docked_to, planets.mars)) - :spawn(), - enterprise = evo.builder() - :name('USS Enterprise') - :set(fragments.spaceship) - :set(evo.pair(relations.docked_to, planets.venus)) - :spawn(), -} - -local queries = { - all_docked_spaceships = evo.builder() - :include(fragments.spaceship) - :include(evo.pair(relations.docked_to, evo.ANY)) - :spawn(), - docked_spaceships_to_mars = evo.builder() - :include(fragments.spaceship) - :include(evo.pair(relations.docked_to, planets.mars)) - :spawn(), - -} - -print '-= | All Docked Spaceships | =-' - -for chunk, entity_list, entity_count in evo.execute(queries.all_docked_spaceships) do - for i = 1, entity_count do - local entity = entity_list[i] - local planet = evo.secondary(entity, relations.docked_to) - print(string.format('%s is docked to %s', evo.name(entity), evo.name(planet))) - end -end - -print '-= | Docked Spaceships to Mars | =-' - -for chunk, entity_list, entity_count in evo.execute(queries.docked_spaceships_to_mars) do - for i = 1, entity_count do - local entity = entity_list[i] - local planet = evo.secondary(entity, relations.docked_to) - print(string.format('%s is docked to %s', evo.name(entity), evo.name(planet))) - end -end diff --git a/develop/testing/name_tests.lua b/develop/testing/name_tests.lua index 8808c2d..21b2e08 100644 --- a/develop/testing/name_tests.lua +++ b/develop/testing/name_tests.lua @@ -3,8 +3,8 @@ local evo = require 'evolved' do local id = evo.id() - local index, version, options = evo.unpack(id) - assert(evo.name(id) == string.format('$%d#%d:%d:%d', id, index, version, options)) + local index, version = evo.unpack(id) + assert(evo.name(id) == string.format('$%d#%d:%d', id, index, version)) evo.set(id, evo.NAME, 'hello') assert(evo.name(id) == 'hello') @@ -13,7 +13,7 @@ do assert(evo.name(id) == 'world') evo.destroy(id) - assert(evo.name(id) == string.format('$%d#%d:%d:%d', id, index, version, options)) + assert(evo.name(id) == string.format('$%d#%d:%d', id, index, version)) end do diff --git a/develop/testing/pairs_tests.lua b/develop/testing/pairs_tests.lua deleted file mode 100644 index 86e4d22..0000000 --- a/develop/testing/pairs_tests.lua +++ /dev/null @@ -1,1920 +0,0 @@ -local evo = require 'evolved' - -do - do - local p, s = evo.unpair(evo.pair(evo.ANY, evo.ANY)) - assert(p == evo.ANY and s == evo.ANY) - end - - do - local f = evo.id() - local p, s = evo.unpair(evo.pair(evo.ANY, f)) - assert(p == evo.ANY and s == f) - end - - do - local f = evo.id() - local p, s = evo.unpair(evo.pair(f, evo.ANY)) - assert(p == f and s == evo.ANY) - end - - do - local fp, fs = evo.id(2) - local p, s = evo.unpair(evo.pair(fp, fs)) - assert(p == fp and s == fs) - end -end - -do - local p, s1, s2 = evo.id(3) - - local e1 = evo.id() - evo.set(e1, evo.pair(p, s1), 11) - - local e12 = evo.id() - evo.set(e12, evo.pair(p, s1), 21) - evo.set(e12, evo.pair(p, s2), 42) - - assert(evo.has(e1, evo.pair(p, s1))) - assert(evo.get(e1, evo.pair(p, s1)) == 11) - assert(evo.has(e12, evo.pair(p, s1))) - assert(evo.get(e12, evo.pair(p, s1)) == 21) - - assert(not evo.has(e1, evo.pair(p, s2))) - assert(evo.get(e1, evo.pair(p, s2)) == nil) - assert(evo.has(e12, evo.pair(p, s2))) - assert(evo.get(e12, evo.pair(p, s2)) == 42) - - assert(evo.has(e1, evo.pair(p, evo.ANY))) - assert(evo.has(e1, evo.pair(evo.ANY, s1))) - assert(not evo.has(e1, evo.pair(evo.ANY, s2))) - assert(evo.has(e12, evo.pair(p, evo.ANY))) - assert(evo.has(e12, evo.pair(evo.ANY, s1))) - assert(evo.has(e12, evo.pair(evo.ANY, s2))) - - assert(not evo.has_all(e1, evo.pair(evo.ANY, s1), evo.pair(evo.ANY, s2))) - assert(evo.has_any(e1, evo.pair(evo.ANY, s1), evo.pair(evo.ANY, s2))) - assert(evo.has_all(e12, evo.pair(evo.ANY, s1), evo.pair(evo.ANY, s2))) - assert(evo.has_any(e12, evo.pair(evo.ANY, s1), evo.pair(evo.ANY, s2))) -end - -do - local p1, p2, s = evo.id(3) - - local e1 = evo.id() - evo.set(e1, evo.pair(p1, s), 11) - - local e12 = evo.id() - evo.set(e12, evo.pair(p1, s), 21) - evo.set(e12, evo.pair(p2, s), 42) - - assert(evo.has(e1, evo.pair(p1, s))) - assert(evo.get(e1, evo.pair(p1, s)) == 11) - assert(evo.has(e12, evo.pair(p1, s))) - assert(evo.get(e12, evo.pair(p1, s)) == 21) - - assert(not evo.has(e1, evo.pair(p2, s))) - assert(evo.get(e1, evo.pair(p2, s)) == nil) - assert(evo.has(e12, evo.pair(p2, s))) - assert(evo.get(e12, evo.pair(p2, s)) == 42) - - assert(evo.has(e1, evo.pair(p1, evo.ANY))) - assert(not evo.has(e1, evo.pair(p2, evo.ANY))) - assert(evo.has(e1, evo.pair(evo.ANY, s))) - assert(evo.has(e12, evo.pair(p1, evo.ANY))) - assert(evo.has(e12, evo.pair(p2, evo.ANY))) - assert(evo.has(e12, evo.pair(evo.ANY, s))) - - assert(not evo.has_all(e1, evo.pair(p1, evo.ANY), evo.pair(p2, evo.ANY))) - assert(evo.has_any(e1, evo.pair(p1, evo.ANY), evo.pair(p2, evo.ANY))) - assert(evo.has_all(e12, evo.pair(p1, evo.ANY), evo.pair(p2, evo.ANY))) - assert(evo.has_any(e12, evo.pair(p1, evo.ANY), evo.pair(p2, evo.ANY))) -end - -do - local p1, s1, p2, s2 = evo.id(4) - evo.set(p1, s1) - evo.set(s1, p1) - evo.set(p2, s2) - assert(evo.empty(evo.pair(p1, s1))) - assert(evo.empty(evo.pair(p2, s2))) - assert(evo.empty_all(evo.pair(p1, s1), evo.pair(p2, s2))) - assert(evo.empty_any(evo.pair(p1, s1), evo.pair(p2, s2))) - assert(not evo.empty_all(evo.pair(p1, s1), evo.pair(p2, s2), p1)) - assert(evo.empty_any(evo.pair(p1, s1), evo.pair(p2, s2), p1)) - assert(evo.empty_all(evo.pair(p1, s1), evo.pair(p2, s2), s2)) - assert(evo.empty_any(evo.pair(p1, s1), evo.pair(p2, s2), s2)) -end - -do - local p1, s1 = evo.id(2) - evo.set(p1, s1) - evo.set(s1, p1) - assert(not evo.has(evo.pair(p1, s1), p1)) - assert(not evo.has(evo.pair(p1, s1), s1)) - assert(not evo.has_all(evo.pair(p1, s1), p1, s1)) - assert(not evo.has_any(evo.pair(p1, s1), p1, s1)) - assert(evo.get(evo.pair(p1, s1), p1) == nil) - assert(evo.get(evo.pair(p1, s1), s1) == nil) -end - -do - local p, s1, s2 = evo.id(3) - - do - local e = evo.builder() - :set(evo.pair(p, s1), 21) - :set(evo.pair(p, s2), 42) - :spawn() - - evo.remove(e, evo.pair(p, s1)) - - assert(not evo.has(e, evo.pair(p, s1))) - assert(evo.get(e, evo.pair(p, s1)) == nil) - - assert(evo.has(e, evo.pair(p, s2))) - assert(evo.get(e, evo.pair(p, s2)) == 42) - - evo.remove(e, evo.pair(p, s2)) - - assert(not evo.has(e, evo.pair(p, s2))) - assert(evo.get(e, evo.pair(p, s2)) == nil) - - assert(not evo.has(e, evo.pair(p, s2))) - assert(evo.get(e, evo.pair(p, s2)) == nil) - end - - do - local e = evo.builder() - :set(evo.pair(p, s1), 21) - :set(evo.pair(p, s2), 42) - :spawn() - - evo.remove(e, evo.pair(p, s2)) - - assert(evo.has(e, evo.pair(p, s1))) - assert(evo.get(e, evo.pair(p, s1)) == 21) - - assert(not evo.has(e, evo.pair(p, s2))) - assert(evo.get(e, evo.pair(p, s2)) == nil) - - evo.remove(e, evo.pair(p, s1)) - - assert(not evo.has(e, evo.pair(p, s1))) - assert(evo.get(e, evo.pair(p, s1)) == nil) - - assert(not evo.has(e, evo.pair(p, s2))) - assert(evo.get(e, evo.pair(p, s2)) == nil) - end -end - -do - local p1, p2, s1, s2 = evo.id(4) - - do - local e = evo.builder() - :set(evo.pair(p1, s1), 11) - :set(evo.pair(p1, s2), 12) - :set(evo.pair(p2, s1), 21) - :set(evo.pair(p2, s2), 22) - :spawn() - - evo.remove(e, evo.pair(p1, evo.ANY)) - - assert(not evo.has(e, evo.pair(p1, s1))) - assert(not evo.has(e, evo.pair(p1, s2))) - assert(not evo.has(e, evo.pair(p1, evo.ANY))) - - assert(evo.has(e, evo.pair(p2, s1))) - assert(evo.get(e, evo.pair(p2, s1)) == 21) - assert(evo.has(e, evo.pair(p2, s2))) - assert(evo.get(e, evo.pair(p2, s2)) == 22) - assert(evo.has(e, evo.pair(p2, evo.ANY))) - end - - do - local e = evo.builder() - :set(evo.pair(p1, s1), 11) - :set(evo.pair(p1, s2), 12) - :set(evo.pair(p2, s1), 21) - :set(evo.pair(p2, s2), 22) - :spawn() - - evo.remove(e, evo.pair(p2, evo.ANY)) - - assert(not evo.has(e, evo.pair(p2, s1))) - assert(not evo.has(e, evo.pair(p2, s2))) - assert(not evo.has(e, evo.pair(p2, evo.ANY))) - - assert(evo.has(e, evo.pair(p1, s1))) - assert(evo.get(e, evo.pair(p1, s1)) == 11) - assert(evo.has(e, evo.pair(p1, s2))) - assert(evo.get(e, evo.pair(p1, s2)) == 12) - assert(evo.has(e, evo.pair(p1, evo.ANY))) - end - - do - local e = evo.builder() - :set(evo.pair(p1, s1), 11) - :set(evo.pair(p1, s2), 12) - :set(evo.pair(p2, s1), 21) - :set(evo.pair(p2, s2), 22) - :spawn() - - evo.remove(e, evo.pair(evo.ANY, s1)) - - assert(not evo.has(e, evo.pair(p1, s1))) - assert(not evo.has(e, evo.pair(p2, s1))) - assert(not evo.has(e, evo.pair(evo.ANY, s1))) - - assert(evo.has(e, evo.pair(p1, s2))) - assert(evo.get(e, evo.pair(p1, s2)) == 12) - assert(evo.has(e, evo.pair(p2, s2))) - assert(evo.get(e, evo.pair(p2, s2)) == 22) - end - - do - local e = evo.builder() - :set(evo.pair(p1, s1), 11) - :set(evo.pair(p1, s2), 12) - :set(evo.pair(p2, s1), 21) - :set(evo.pair(p2, s2), 22) - :spawn() - - evo.remove(e, evo.pair(evo.ANY, s2)) - - assert(not evo.has(e, evo.pair(p1, s2))) - assert(not evo.has(e, evo.pair(p2, s2))) - assert(not evo.has(e, evo.pair(evo.ANY, s2))) - - assert(evo.has(e, evo.pair(p1, s1))) - assert(evo.get(e, evo.pair(p1, s1)) == 11) - assert(evo.has(e, evo.pair(p2, s1))) - assert(evo.get(e, evo.pair(p2, s1)) == 21) - end - - do - local e = evo.builder() - :set(evo.pair(p1, s1), 11) - :set(evo.pair(p1, s2), 12) - :set(evo.pair(p2, s1), 21) - :set(evo.pair(p2, s2), 22) - :set(p1, s1) - :set(p2, s2) - :spawn() - - evo.remove(e, evo.pair(evo.ANY, evo.ANY)) - - assert(not evo.has(e, evo.pair(p1, s1))) - assert(not evo.has(e, evo.pair(p1, s2))) - assert(not evo.has(e, evo.pair(p2, s1))) - assert(not evo.has(e, evo.pair(p2, s2))) - - assert(evo.has(e, p1) and evo.get(e, p1) == s1) - assert(evo.has(e, p2) and evo.get(e, p2) == s2) - end -end - -do - local p1, s1, p2, s2 = evo.id(4) - - local e = evo.builder() - :set(evo.pair(p1, s1), 42) - :spawn() - - evo.remove(e, evo.pair(p2, evo.ANY)) - evo.remove(e, evo.pair(evo.ANY, s2)) - - assert(evo.has(e, evo.pair(p1, s1))) - assert(evo.get(e, evo.pair(p1, s1)) == 42) - - evo.remove(e, evo.pair(p1, s1)) - - assert(not evo.has(e, evo.pair(p1, s1))) - assert(evo.get(e, evo.pair(p1, s1)) == nil) -end - -do - local f1, f2, f3, p1, s1, p2, s2 = evo.id(7) - evo.set(f1, evo.REQUIRES, { f2 }) - evo.set(f2, evo.DEFAULT, 84) - evo.set(f2, evo.REQUIRES, { evo.pair(p2, s2) }) - evo.set(p1, evo.REQUIRES, { f3 }) - evo.set(s1, evo.REQUIRES, { f3 }) - evo.set(p2, evo.REQUIRES, { f3 }) - evo.set(s2, evo.REQUIRES, { f3 }) - - local e = evo.builder() - :set(f1, 21) - :set(evo.pair(p1, s1), 42) - :spawn() - - assert(evo.has(e, evo.pair(p1, s1))) - assert(evo.get(e, evo.pair(p1, s1)) == 42) - - assert(evo.has(e, evo.pair(p2, s2))) - assert(evo.get(e, evo.pair(p2, s2)) == true) - - assert(evo.has(e, f1)) - assert(evo.get(e, f1) == 21) - - assert(evo.has(e, f2)) - assert(evo.get(e, f2) == 84) - - assert(evo.has(e, f3)) - assert(evo.get(e, f3) == true) -end - -do - local p1, p2, s1, s2 = evo.id(4) - - do - local e1 = evo.builder() - :set(evo.pair(p1, s1)) - :set(evo.pair(p1, s2)) - :spawn() - - local e2 = evo.clone(e1) - - evo.remove(e1, evo.pair(p1, evo.ANY)) - evo.remove(e2, evo.pair(p1, evo.ANY)) - assert(evo.empty_all(e1, e2)) - end - - do - local e1 = evo.builder() - :set(evo.pair(p1, s1)) - :set(evo.pair(p2, s1)) - :spawn() - - local e2 = evo.clone(e1) - - evo.remove(e1, evo.pair(evo.ANY, s1)) - evo.remove(e2, evo.pair(evo.ANY, s1)) - assert(evo.empty_all(e1, e2)) - end - - do - local e1 = evo.builder() - :set(evo.pair(p1, s1)) - :set(evo.pair(p1, s2)) - :set(evo.pair(p2, s1)) - :set(evo.pair(p2, s2)) - :spawn() - - local e2 = evo.clone(e1) - - evo.remove(e1, evo.pair(evo.ANY, evo.ANY)) - evo.remove(e2, evo.pair(evo.ANY, evo.ANY)) - assert(evo.empty_all(e1, e2)) - end -end - -do - local f, p1, p2, s1, s2 = evo.id(5) - - do - local e1 = evo.builder() - :set(f, 42) - :set(evo.pair(p1, s1)) - :set(evo.pair(p1, s2)) - :spawn() - - local e2 = evo.clone(e1) - - evo.remove(e1, evo.pair(p1, evo.ANY)) - evo.remove(e2, evo.pair(p1, evo.ANY)) - - assert(evo.has(e1, f) and evo.has(e2, f)) - assert(not evo.has(e1, evo.pair(evo.ANY, evo.ANY))) - end - - do - local e1 = evo.builder() - :set(f, 42) - :set(evo.pair(p1, s1)) - :set(evo.pair(p2, s1)) - :spawn() - - local e2 = evo.clone(e1) - - evo.remove(e1, evo.pair(evo.ANY, s1)) - evo.remove(e2, evo.pair(evo.ANY, s1)) - - assert(evo.has(e1, f) and evo.has(e2, f)) - assert(not evo.has(e1, evo.pair(evo.ANY, evo.ANY))) - end - - do - local e1 = evo.builder() - :set(f, 42) - :set(evo.pair(p1, s1)) - :set(evo.pair(p1, s2)) - :set(evo.pair(p2, s1)) - :set(evo.pair(p2, s2)) - :spawn() - - local e2 = evo.clone(e1) - - evo.remove(e1, evo.pair(evo.ANY, evo.ANY)) - evo.remove(e2, evo.pair(evo.ANY, evo.ANY)) - - assert(evo.has(e1, f) and evo.has(e2, f)) - assert(not evo.has(e1, evo.pair(evo.ANY, evo.ANY))) - end -end - -do - do - local p, s = evo.id(2) - evo.set(p, evo.NAME, 'p') - evo.set(s, evo.NAME, 's') - local ps_chunk = evo.chunk(evo.pair(p, s)) - assert(tostring(ps_chunk) == '<${p,s}>') - end - do - local p, s = evo.id(2) - local ps_chunk = evo.chunk(evo.pair(p, s)) - evo.set(p, evo.NAME, 'p') - evo.set(s, evo.NAME, 's') - evo.destroy(p) - assert(tostring(ps_chunk) ~= '<${p,s}>') - end - do - local p, s = evo.id(2) - local ps_chunk = evo.chunk(evo.pair(p, s)) - evo.set(p, evo.NAME, 'p') - evo.set(s, evo.NAME, 's') - evo.destroy(s) - assert(tostring(ps_chunk) ~= '<${p,s}>') - end - do - local p, s = evo.id(2) - local ps_chunk = evo.chunk(evo.pair(p, s)) - evo.set(p, evo.NAME, 'p') - evo.set(s, evo.NAME, 's') - evo.destroy(p, s) - assert(tostring(ps_chunk) ~= '<${p,s}>') - end -end - -do - do - local p, s = evo.id(2) - local ps = evo.pair(evo.ANY, s) - local e = evo.id() - evo.set(e, p, 42) - evo.destroy(s) - evo.remove(e, ps) - end - - do - local p, s = evo.id(2) - local ps = evo.pair(p, evo.ANY) - local e = evo.id() - evo.set(e, s, 42) - evo.destroy(p) - evo.remove(e, ps) - end -end - -do - local p, s = evo.id(2) - - local e = evo.id() - assert(not evo.has(e, evo.pair(p, s))) - assert(not evo.has(e, evo.pair(p, evo.ANY))) - assert(not evo.has(e, evo.pair(evo.ANY, s))) - assert(not evo.has(e, evo.pair(evo.ANY, evo.ANY))) - - evo.set(e, p) - assert(not evo.has(e, evo.pair(p, s))) - assert(not evo.has(e, evo.pair(p, evo.ANY))) - assert(not evo.has(e, evo.pair(evo.ANY, s))) - assert(not evo.has(e, evo.pair(evo.ANY, evo.ANY))) - - evo.set(e, s) - assert(not evo.has(e, evo.pair(p, s))) - assert(not evo.has(e, evo.pair(p, evo.ANY))) - assert(not evo.has(e, evo.pair(evo.ANY, s))) - assert(not evo.has(e, evo.pair(evo.ANY, evo.ANY))) - - evo.set(e, evo.pair(p, s)) - assert(evo.has(e, evo.pair(p, s))) - assert(evo.has(e, evo.pair(p, evo.ANY))) - assert(evo.has(e, evo.pair(evo.ANY, s))) - assert(evo.has(e, evo.pair(evo.ANY, evo.ANY))) -end - -do - local p1, s1, p2, s2 = evo.id(4) - - local e = evo.builder():set(evo.pair(p1, s1)):spawn() - assert(evo.has(e, evo.pair(p1, s1))) - assert(evo.has(e, evo.pair(p1, evo.ANY))) - assert(evo.has(e, evo.pair(evo.ANY, s1))) - assert(evo.has(e, evo.pair(evo.ANY, evo.ANY))) - assert(not evo.has(e, evo.pair(p1, s2))) - assert(not evo.has(e, evo.pair(p2, s1))) - assert(not evo.has(e, evo.pair(p2, s2))) - assert(not evo.has(e, evo.pair(p2, evo.ANY))) - assert(not evo.has(e, evo.pair(evo.ANY, s2))) - - evo.set(e, evo.pair(p2, s2)) - - assert(evo.has(e, evo.pair(p1, s1))) - assert(evo.has(e, evo.pair(p1, evo.ANY))) - assert(evo.has(e, evo.pair(evo.ANY, s1))) - assert(evo.has(e, evo.pair(evo.ANY, evo.ANY))) - assert(not evo.has(e, evo.pair(p1, s2))) - assert(not evo.has(e, evo.pair(p2, s1))) - assert(evo.has(e, evo.pair(p2, s2))) - assert(evo.has(e, evo.pair(p2, evo.ANY))) - assert(evo.has(e, evo.pair(evo.ANY, s2))) -end - -do - local p1, s1, s2 = evo.id(3) - - do - local e = evo.builder() - :set(evo.pair(p1, s1), 42) - :spawn() - - evo.set(e, evo.pair(p1, s1), 84) - assert(evo.get(e, evo.pair(p1, s1)) == 84) - assert(evo.get(e, evo.pair(p1, s2)) == nil) - - evo.set(e, evo.pair(p1, s2), 42) - assert(evo.get(e, evo.pair(p1, s1)) == 84) - assert(evo.get(e, evo.pair(p1, s2)) == 42) - end -end - -do - local p1, s1, p2, s2 = evo.id(4) - - do - local e = evo.builder() - :set(evo.pair(p1, s1), 42) - :set(evo.pair(p1, s2), 84) - :set(evo.pair(p2, s1), 21) - :set(evo.pair(p2, s2), 63) - :spawn() - - evo.remove(e, evo.pair(p1, evo.ANY)) - assert(not evo.has(e, evo.pair(p1, s1))) - assert(evo.get(e, evo.pair(p1, s1)) == nil) - assert(not evo.has(e, evo.pair(p1, s2))) - assert(evo.get(e, evo.pair(p1, s2)) == nil) - assert(evo.has(e, evo.pair(p2, s1))) - assert(evo.get(e, evo.pair(p2, s1)) == 21) - assert(evo.has(e, evo.pair(p2, s2))) - assert(evo.get(e, evo.pair(p2, s2)) == 63) - end - - do - local e = evo.builder() - :set(evo.pair(p1, s1), 42) - :set(evo.pair(p1, s2), 84) - :set(evo.pair(p2, s1), 21) - :set(evo.pair(p2, s2), 63) - :spawn() - - evo.remove(e, evo.pair(evo.ANY, s2)) - assert(evo.has(e, evo.pair(p1, s1))) - assert(evo.get(e, evo.pair(p1, s1)) == 42) - assert(not evo.has(e, evo.pair(p1, s2))) - assert(evo.get(e, evo.pair(p1, s2)) == nil) - assert(evo.has(e, evo.pair(p2, s1))) - assert(evo.get(e, evo.pair(p2, s1)) == 21) - assert(not evo.has(e, evo.pair(p2, s2))) - assert(evo.get(e, evo.pair(p2, s2)) == nil) - end -end - -do - local p1, p2, s1, s2 = evo.id(4) - - ---@param o evolved.entity - ---@param s evolved.fragment - ---@return evolved.fragment[], evolved.component[], number - local function collect_primaries(o, s) - local fragments, components, count = {}, {}, 0 - - for f, c in evo.primaries(o, s) do - count = count + 1 - - fragments[count] = f - components[count] = c - - do - local ff, cc = evo.primary(o, s, count) - assert(ff == f and cc == c) - end - end - - assert(evo.primary_count(o, s) == count) - return fragments, components, count - end - - ---@param o evolved.entity - ---@param p evolved.fragment - ---@return evolved.fragment[], evolved.component[], number - local function collect_secondaries(o, p) - local fragments, components, count = {}, {}, 0 - - for f, c in evo.secondaries(o, p) do - count = count + 1 - fragments[count] = f - components[count] = c - end - - return fragments, components, count - end - - do - local e = evo.builder() - :set(evo.pair(p1, s1), 42) - :spawn() - - assert(evo.primary(e, s1) == p1) - assert(evo.primary(e, s2) == nil) - - assert(evo.secondary(e, p1) == s1) - assert(evo.secondary(e, p2) == nil) - - assert(evo.primary_count(e, s1) == 1) - assert(evo.primary_count(e, s2) == 0) - assert(evo.secondary_count(e, p1) == 1) - assert(evo.secondary_count(e, p2) == 0) - - do - local p_list, c_list, count = collect_primaries(e, s1) - assert(#p_list == 1 and #c_list == 1 and count == 1) - assert(p_list[1] == p1 and c_list[1] == 42) - end - - do - local p_list, c_list, count = collect_primaries(e, s2) - assert(#p_list == 0 and #c_list == 0 and count == 0) - end - - do - local s_list, c_list, count = collect_secondaries(e, p1) - assert(#s_list == 1 and #c_list == 1 and count == 1) - assert(s_list[1] == s1 and c_list[1] == 42) - end - - do - local s_list, c_list, count = collect_secondaries(e, p2) - assert(#s_list == 0 and #c_list == 0 and count == 0) - end - end - - do - local e = evo.builder() - :set(evo.pair(p1, s1), 42) - :set(evo.pair(p1, s2), 84) - :set(evo.pair(p2, s1), 21) - :set(evo.pair(p2, s2), 63) - :spawn() - - do - assert(evo.primary_count(e, s1) == 2) - assert(evo.primary_count(e, s2) == 2) - assert(evo.secondary_count(e, p1) == 2) - assert(evo.secondary_count(e, p2) == 2) - end - - do - local pp, cc = evo.primary(e, s1) - assert(pp == p1 and cc == 42) - - pp, cc = evo.primary(e, s1, 1) - assert(pp == p1 and cc == 42) - - pp, cc = evo.primary(e, s1, 2) - assert(pp == p2 and cc == 21) - - pp, cc = evo.primary(e, s1, 3) - assert(pp == nil and cc == nil) - end - - do - local pp, cc = evo.primary(e, s2) - assert(pp == p1 and cc == 84) - - pp, cc = evo.primary(e, s2, 1) - assert(pp == p1 and cc == 84) - - pp, cc = evo.primary(e, s2, 2) - assert(pp == p2 and cc == 63) - - pp, cc = evo.primary(e, s2, 3) - assert(pp == nil and cc == nil) - end - - do - local pp, cc = evo.secondary(e, p1) - assert(pp == s1 and cc == 42) - - pp, cc = evo.secondary(e, p1, 1) - assert(pp == s1 and cc == 42) - - pp, cc = evo.secondary(e, p1, 2) - assert(pp == s2 and cc == 84) - - pp, cc = evo.secondary(e, p1, 3) - assert(pp == nil and cc == nil) - end - - do - local pp, cc = evo.secondary(e, p2) - assert(pp == s1 and cc == 21) - - pp, cc = evo.secondary(e, p2, 1) - assert(pp == s1 and cc == 21) - - pp, cc = evo.secondary(e, p2, 2) - assert(pp == s2 and cc == 63) - - pp, cc = evo.secondary(e, p2, 3) - assert(pp == nil and cc == nil) - end - - do - local p_list, c_list, count = collect_primaries(e, s1) - assert(#p_list == 2 and #c_list == 2 and count == 2) - assert(p_list[1] == p1 and c_list[1] == 42) - assert(p_list[2] == p2 and c_list[2] == 21) - end - - do - local p_list, c_list, count = collect_primaries(e, s2) - assert(#p_list == 2 and #c_list == 2 and count == 2) - assert(p_list[1] == p1 and c_list[1] == 84) - assert(p_list[2] == p2 and c_list[2] == 63) - end - - do - local s_list, c_list, count = collect_secondaries(e, p1) - assert(#s_list == 2 and #c_list == 2 and count == 2) - assert(s_list[1] == s1 and c_list[1] == 42) - assert(s_list[2] == s2 and c_list[2] == 84) - end - - do - local s_list, c_list, count = collect_secondaries(e, p2) - assert(#s_list == 2 and #c_list == 2 and count == 2) - assert(s_list[1] == s1 and c_list[1] == 21) - assert(s_list[2] == s2 and c_list[2] == 63) - end - end -end - -do - local p, s = evo.id(2) - - local e = evo.id() - - assert(not evo.primary(e, s)) - assert(not evo.primary(e, s, 1)) - assert(not evo.primary(e, s, 2)) - assert(not evo.primary(e, s, 0)) - assert(not evo.primary(e, s, -1)) - assert(not evo.primary(e, s, -2)) - - assert(not evo.secondary(e, p)) - assert(not evo.secondary(e, p, 1)) - assert(not evo.secondary(e, p, 2)) - assert(not evo.secondary(e, p, 0)) - assert(not evo.secondary(e, p, -1)) - assert(not evo.secondary(e, p, -2)) - - assert(evo.primary_count(e, s) == 0) - assert(evo.secondary_count(e, p) == 0) - - assert(evo.primaries(e, s)() == nil) - assert(evo.secondaries(e, p)() == nil) -end - -do - local p1, p2, s1, s2 = evo.id(4) - - local e = evo.builder() - :set(evo.pair(p1, s1), 42) - :set(evo.pair(p1, s2), 84) - :set(evo.pair(p2, s1), 21) - :set(evo.pair(p2, s2), 63) - :spawn() - - assert(evo.primary(e, evo.ANY) == nil) - assert(evo.primary(e, evo.ANY, 1) == nil) - assert(evo.primary(e, evo.ANY, 2) == nil) - - assert(evo.secondary(e, evo.ANY) == nil) - assert(evo.secondary(e, evo.ANY, 1) == nil) - assert(evo.secondary(e, evo.ANY, 2) == nil) - - assert(evo.primaries(e, evo.ANY)() == nil) - assert(evo.secondaries(e, evo.ANY)() == nil) - - assert(evo.primary_count(e, evo.ANY) == 0) - assert(evo.secondary_count(e, evo.ANY) == 0) -end - -do - do - local p, s = evo.id(2) - - local e = evo.builder() - :set(s) - :set(evo.pair(p, s), 42) - :spawn() - - evo.destroy(p) - assert(evo.alive(e)) - assert(evo.has(e, s)) - assert(not evo.has(e, evo.pair(p, s))) - assert(evo.get(e, evo.pair(p, s)) == nil) - end - - do - local p, s = evo.id(2) - - local e = evo.builder() - :set(p) - :set(evo.pair(p, s), 42) - :spawn() - - evo.destroy(s) - assert(evo.alive(e)) - assert(evo.has(e, p)) - assert(not evo.has(e, evo.pair(p, s))) - assert(evo.get(e, evo.pair(p, s)) == nil) - end - - do - local p, s = evo.id(2) - evo.set(p, evo.DESTRUCTION_POLICY, evo.DESTRUCTION_POLICY_DESTROY_ENTITY) - - local e = evo.builder() - :set(evo.pair(p, s), 42) - :spawn() - - evo.destroy(p) - assert(not evo.alive(e)) - end - - do - local p, s = evo.id(2) - evo.set(s, evo.DESTRUCTION_POLICY, evo.DESTRUCTION_POLICY_DESTROY_ENTITY) - - local e = evo.builder() - :set(evo.pair(p, s), 42) - :spawn() - - evo.destroy(s) - assert(not evo.alive(e)) - end -end - -do - local p1, s1 = evo.id(2) - - local e0 = evo.builder() - :destruction_policy(evo.DESTRUCTION_POLICY_DESTROY_ENTITY) - :spawn() - - local e1 = evo.builder() - :set(evo.pair(p1, e0), 11) - :destruction_policy(evo.DESTRUCTION_POLICY_DESTROY_ENTITY) - :spawn() - - local e2 = evo.builder() - :set(evo.pair(e1, s1), 22) - :destruction_policy(evo.DESTRUCTION_POLICY_DESTROY_ENTITY) - :spawn() - - local e3 = evo.builder() - :set(evo.pair(e2, e2), 33) - :spawn() - - evo.destroy(e0) - assert(not evo.alive(e1)) - assert(not evo.alive(e2)) - assert(not evo.alive(e3)) -end - -do - do - local f, p, s = evo.id(3) - - local e = evo.builder():set(f, 21):set(evo.pair(p, s), 42):spawn() - - evo.destroy(f) - assert(evo.get(e, f) == nil) - assert(evo.get(e, evo.pair(p, s)) == 42) - end - - do - local f, p, s = evo.id(3) - - local e = evo.builder():set(f, 21):set(evo.pair(p, s), 42):spawn() - - evo.destroy(p) - assert(evo.get(e, f) == 21) - assert(evo.get(e, evo.pair(p, s)) == nil) - end - - do - local f, p, s = evo.id(3) - - local e = evo.builder():set(f, 21):set(evo.pair(p, s), 42):spawn() - - evo.destroy(s) - assert(evo.get(e, f) == 21) - assert(evo.get(e, evo.pair(p, s)) == nil) - end - - do - local f, p, s = evo.id(3) - - local e = evo.builder():set(f, 21):set(evo.pair(p, s), 42):spawn() - - evo.destroy(p, s) - assert(evo.get(e, f) == 21) - assert(evo.get(e, evo.pair(p, s)) == nil) - end - - do - local f, p, s = evo.id(3) - - local e = evo.builder():set(f, 21):set(evo.pair(p, s), 42):spawn() - - evo.destroy(f, p, s) - assert(evo.alive(e) and evo.empty(e)) - end -end - -do - do - local f, p, s = evo.id(3) - evo.set(p, evo.DESTRUCTION_POLICY, evo.DESTRUCTION_POLICY_DESTROY_ENTITY) - - local e = evo.builder():set(f, 21):set(evo.pair(p, s), 42):spawn() - - evo.destroy(p) - assert(not evo.alive(e)) - end - - do - local f, p, s = evo.id(3) - evo.set(s, evo.DESTRUCTION_POLICY, evo.DESTRUCTION_POLICY_DESTROY_ENTITY) - - local e = evo.builder():set(f, 21):set(evo.pair(p, s), 42):spawn() - - evo.destroy(p) - assert(evo.get(e, f) == 21) - assert(evo.get(e, evo.pair(p, s)) == nil) - end - - do - local f, p, s = evo.id(3) - evo.set(p, evo.DESTRUCTION_POLICY, evo.DESTRUCTION_POLICY_DESTROY_ENTITY) - - local e = evo.builder():set(f, 21):set(evo.pair(p, s), 42):spawn() - - evo.destroy(s) - assert(evo.get(e, f) == 21) - assert(evo.get(e, evo.pair(p, s)) == nil) - end - - do - local f, p, s = evo.id(3) - evo.set(s, evo.DESTRUCTION_POLICY, evo.DESTRUCTION_POLICY_DESTROY_ENTITY) - - local e = evo.builder():set(f, 21):set(evo.pair(p, s), 42):spawn() - - evo.destroy(s) - assert(not evo.alive(e)) - end -end - -do - local f, p, s = evo.id(3) - - local e = evo.builder():set(f, 21):set(evo.pair(p, s), 42):spawn() - - evo.destroy(evo.pair(p, s)) - evo.destroy(evo.pair(evo.ANY, s)) - evo.destroy(evo.pair(p, evo.ANY)) - evo.destroy(evo.pair(evo.ANY, evo.ANY)) - - assert(evo.get(e, f) == 21) - assert(evo.get(e, evo.pair(p, s)) == 42) -end - -do - evo.collect_garbage() - - local f, p1, s1, p2, s2 = evo.id(5) - - local e1 = evo.builder() - :set(f, 21) - :set(evo.pair(p1, s1), 42) - :set(evo.pair(p2, s2), 84) - :spawn() - - local e2 = evo.builder() - :set(f, 21) - :set(evo.pair(p1, s1), 42) - :set(evo.pair(p2, s2), 84) - :spawn() - - local f_chunk = evo.chunk(f) - local f_p2s2_chunk = evo.chunk(f, evo.pair(p2, s2)) - local f_p1s1_p2s2_chunk = evo.chunk(f, evo.pair(p1, s1), evo.pair(p2, s2)) - - assert(f_p1s1_p2s2_chunk:entities()[1] == e1) - assert(f_p1s1_p2s2_chunk:entities()[2] == e2) - - evo.remove(e1, evo.pair(p1, evo.ANY)) - - assert(f_p2s2_chunk:entities()[1] == e1) - assert(f_p1s1_p2s2_chunk:entities()[1] == e2) - - evo.remove(e1, evo.pair(p2, evo.ANY)) - - assert(f_chunk:entities()[1] == e1) - assert(f_p1s1_p2s2_chunk:entities()[1] == e2) - - evo.collect_garbage() - - assert(f_chunk:alive()) - assert(not f_p2s2_chunk:alive()) - assert(f_p1s1_p2s2_chunk:alive()) - - evo.remove(e2, evo.pair(p1, evo.ANY)) - - local new_f_p2s2_chunk = evo.chunk(f, evo.pair(p2, s2)) - assert(new_f_p2s2_chunk:entities()[1] == e2) -end - -do - evo.collect_garbage() - - local f, p1, p2, s1, s2 = evo.id(5) - - local e1 = evo.builder() - :set(f, 21) - :set(evo.pair(p1, s1), 42) - :set(evo.pair(p2, s2), 84) - :spawn() - - local f_p1s1_p2s2_chunk = evo.chunk(f, evo.pair(p1, s1), evo.pair(p2, s2)) - assert(f_p1s1_p2s2_chunk:entities()[1] == e1) - - evo.destroy(p2, s2) - - evo.collect_garbage() - - local f_p1s1_chunk = evo.chunk(f, evo.pair(p1, s1)) - assert(f_p1s1_chunk:entities()[1] == e1) -end - -do - local f, p, s = evo.id(3) - evo.set(p, evo.DEFAULT, 42) - - do - local e = evo.id() - evo.set(e, f) - evo.set(e, evo.pair(p, s)) - assert(evo.has(e, f) and evo.get(e, f) == true) - assert(evo.has(e, evo.pair(p, s)) and evo.get(e, evo.pair(p, s)) == 42) - end - - do - local e = evo.builder():set(f):set(evo.pair(p, s)):spawn() - assert(evo.has(e, f) and evo.get(e, f) == true) - assert(evo.has(e, evo.pair(p, s)) and evo.get(e, evo.pair(p, s)) == 42) - end - - do - local e = evo.builder():set(f, 84):set(evo.pair(p, s), 21):spawn() - evo.set(e, f) - evo.set(e, evo.pair(p, s)) - assert(evo.has(e, f) and evo.get(e, f) == true) - assert(evo.has(e, evo.pair(p, s)) and evo.get(e, evo.pair(p, s)) == 42) - end -end - -do - do - local f, p, s = evo.id(3) - assert(evo.empty(evo.pair(p, s))) - - evo.set(p, f) - assert(evo.empty(evo.pair(p, s))) - - evo.destroy(p) - assert(evo.empty(evo.pair(p, s))) - end - - do - local f, p, s = evo.id(3) - assert(evo.empty(evo.pair(p, s))) - - evo.set(p, f) - assert(evo.empty(evo.pair(p, s))) - - evo.destroy(s) - assert(evo.empty(evo.pair(p, s))) - - evo.destroy(p) - assert(evo.empty(evo.pair(p, s))) - end - - do - local f, p, s = evo.id(3) - assert(not evo.has(p, f)) - assert(not evo.has(evo.pair(p, s), f)) - - evo.set(p, f, 42) - assert(evo.has(p, f)) - assert(not evo.has(evo.pair(p, s), f)) - assert(not evo.has(evo.pair(s, p), f)) - assert(evo.get(p, f) == 42) - assert(evo.get(evo.pair(p, s), f) == nil) - assert(evo.get(evo.pair(s, p), f) == nil) - end -end - -do - local p, s = evo.id(3) - - local set_count = 0 - local insert_count = 0 - local remove_count = 0 - - evo.set(p, evo.ON_SET, function(e, f, nc, oc) - set_count = set_count + 1 - assert(f == p or f == evo.pair(p, s)) - assert(nc == 21 or nc == 42) - assert(oc == nil or oc == 21) - assert(evo.has(e, f)) - assert(evo.get(e, f) == nc) - end) - - evo.set(p, evo.ON_INSERT, function(e, f, nc) - insert_count = insert_count + 1 - assert(f == p or f == evo.pair(p, s)) - assert(nc == 21 or nc == 42) - assert(evo.has(e, f)) - assert(evo.get(e, f) == nc) - end) - - evo.set(p, evo.ON_REMOVE, function(e, f, oc) - remove_count = remove_count + 1 - assert(f == p or f == evo.pair(p, s)) - assert(oc == 21 or oc == 42) - assert(not evo.has(e, f)) - end) - - do - set_count, insert_count, remove_count = 0, 0, 0 - local e = evo.id() - evo.set(e, p, 21) - evo.set(e, evo.pair(p, s), 42) - assert(set_count == 2) - assert(insert_count == 2) - assert(remove_count == 0) - evo.remove(e, p) - assert(set_count == 2) - assert(insert_count == 2) - assert(remove_count == 1) - evo.remove(e, evo.pair(p, s)) - assert(set_count == 2) - assert(insert_count == 2) - assert(remove_count == 2) - end - - do - set_count, insert_count, remove_count = 0, 0, 0 - local e = evo.id() - evo.set(e, p, 21) - evo.set(e, evo.pair(p, s), 42) - assert(set_count == 2) - assert(insert_count == 2) - assert(remove_count == 0) - evo.destroy(e) - assert(set_count == 2) - assert(insert_count == 2) - assert(remove_count == 2) - end -end - -do - do - local f, p, s = evo.id(3) - evo.set(p, evo.DESTRUCTION_POLICY, evo.DESTRUCTION_POLICY_DESTROY_ENTITY) - - local e = evo.builder() - :set(f, 21) - :set(evo.pair(p, s), 42) - :spawn() - - evo.destroy(p) - - assert(not evo.alive(e) and evo.empty(e)) - assert(not evo.has(e, f) and evo.get(e, f) == nil) - assert(not evo.has(e, evo.pair(p, s)) and evo.get(e, evo.pair(p, s)) == nil) - end - - do - local f, p, s = evo.id(3) - evo.set(p, evo.DESTRUCTION_POLICY, evo.DESTRUCTION_POLICY_DESTROY_ENTITY) - - local e = evo.builder() - :set(f, 21) - :set(evo.pair(p, s), 42) - :spawn() - - evo.destroy(s) - - assert(evo.alive(e) and not evo.empty(e)) - assert(evo.has(e, f) and evo.get(e, f) == 21) - assert(not evo.has(e, evo.pair(p, s)) and evo.get(e, evo.pair(p, s)) == nil) - end - - do - local f, p, s = evo.id(3) - evo.set(p, evo.DESTRUCTION_POLICY, evo.DESTRUCTION_POLICY_REMOVE_FRAGMENT) - - local e = evo.builder() - :set(f, 21) - :set(evo.pair(p, s), 42) - :spawn() - - evo.destroy(p) - - assert(evo.alive(e) and not evo.empty(e)) - assert(evo.has(e, f) and evo.get(e, f) == 21) - assert(not evo.has(e, evo.pair(p, s)) and evo.get(e, evo.pair(p, s)) == nil) - end - - do - local f, p, s = evo.id(3) - evo.set(p, evo.DESTRUCTION_POLICY, evo.DESTRUCTION_POLICY_REMOVE_FRAGMENT) - - local e = evo.builder() - :set(f, 21) - :set(evo.pair(p, s), 42) - :spawn() - - evo.destroy(s) - - assert(evo.alive(e) and not evo.empty(e)) - assert(evo.has(e, f) and evo.get(e, f) == 21) - assert(not evo.has(e, evo.pair(p, s)) and evo.get(e, evo.pair(p, s)) == nil) - end - - do - local f, p, s = evo.id(3) - evo.set(p, evo.DESTRUCTION_POLICY, evo.DESTRUCTION_POLICY_REMOVE_FRAGMENT) - evo.set(s, evo.DESTRUCTION_POLICY, evo.DESTRUCTION_POLICY_DESTROY_ENTITY) - - local e = evo.builder() - :set(f, 21) - :set(evo.pair(p, s), 42) - :spawn() - - evo.destroy(p) - - assert(evo.alive(e) and not evo.empty(e)) - assert(evo.has(e, f) and evo.get(e, f) == 21) - assert(not evo.has(e, evo.pair(p, s)) and evo.get(e, evo.pair(p, s)) == nil) - end - - do - local f, p, s = evo.id(3) - evo.set(p, evo.DESTRUCTION_POLICY, evo.DESTRUCTION_POLICY_REMOVE_FRAGMENT) - evo.set(s, evo.DESTRUCTION_POLICY, evo.DESTRUCTION_POLICY_DESTROY_ENTITY) - - local e = evo.builder() - :set(f, 21) - :set(evo.pair(p, s), 42) - :spawn() - - evo.destroy(s) - - assert(not evo.alive(e) and evo.empty(e)) - assert(not evo.has(e, f) and evo.get(e, f) == nil) - assert(not evo.has(e, evo.pair(p, s)) and evo.get(e, evo.pair(p, s)) == nil) - end -end - -do - do - local p, s = evo.id(2) - evo.set(p, evo.DEFAULT, 42) - - do - local e = evo.id() - evo.set(e, evo.pair(p, s)) - assert(evo.has(e, evo.pair(p, s)) and evo.get(e, evo.pair(p, s)) == 42) - end - - do - local e = evo.builder():set(evo.pair(p, s)):spawn() - assert(evo.has(e, evo.pair(p, s)) and evo.get(e, evo.pair(p, s)) == 42) - end - end - - do - local p, s = evo.id(2) - evo.set(s, evo.DEFAULT, 21) - - do - local e = evo.id() - evo.set(e, evo.pair(p, s)) - assert(evo.has(e, evo.pair(p, s)) and evo.get(e, evo.pair(p, s)) == true) - end - - do - local e = evo.builder():set(evo.pair(p, s)):spawn() - assert(evo.has(e, evo.pair(p, s)) and evo.get(e, evo.pair(p, s)) == true) - end - end - - do - local p, s = evo.id(2) - evo.set(p, evo.DEFAULT, 42) - evo.set(s, evo.DEFAULT, 21) - - do - local e = evo.id() - evo.set(e, evo.pair(p, s)) - assert(evo.has(e, evo.pair(p, s)) and evo.get(e, evo.pair(p, s)) == 42) - end - - do - local e = evo.builder():set(evo.pair(p, s)):spawn() - assert(evo.has(e, evo.pair(p, s)) and evo.get(e, evo.pair(p, s)) == 42) - end - end -end - -do - do - local f, p, s = evo.id(3) - evo.set(p, evo.REQUIRES, { f }) - - do - local e = evo.id() - evo.set(e, evo.pair(p, s)) - assert(evo.has(e, f) and evo.get(e, f) == true) - assert(evo.has(e, evo.pair(p, s)) and evo.get(e, evo.pair(p, s)) == true) - end - - do - local e = evo.builder():set(evo.pair(p, s)):spawn() - assert(evo.has(e, f) and evo.get(e, f) == true) - assert(evo.has(e, evo.pair(p, s)) and evo.get(e, evo.pair(p, s)) == true) - end - end - - do - local f, p, s = evo.id(3) - evo.set(s, evo.REQUIRES, { f }) - - do - local e = evo.id() - evo.set(e, evo.pair(p, s)) - assert(not evo.has(e, f) and evo.get(e, f) == nil) - assert(evo.has(e, evo.pair(p, s)) and evo.get(e, evo.pair(p, s)) == true) - end - - do - local e = evo.builder():set(evo.pair(p, s)):spawn() - assert(not evo.has(e, f) and evo.get(e, f) == nil) - assert(evo.has(e, evo.pair(p, s)) and evo.get(e, evo.pair(p, s)) == true) - end - end - - do - local f, p, s = evo.id(3) - evo.set(p, evo.REQUIRES, { f }) - evo.set(f, evo.REQUIRES, { evo.pair(s, p) }) - evo.set(s, evo.REQUIRES, { p }) - - do - local e = evo.id() - evo.set(e, evo.pair(p, s)) - assert(evo.has(e, f) and evo.get(e, f) == true) - assert(evo.has(e, evo.pair(p, s)) and evo.get(e, evo.pair(p, s)) == true) - assert(evo.has(e, evo.pair(s, p)) and evo.get(e, evo.pair(s, p)) == true) - assert(evo.has(e, p) and evo.get(e, p) == true) - end - - do - local e = evo.builder():set(evo.pair(p, s)):spawn() - assert(evo.has(e, f) and evo.get(e, f) == true) - assert(evo.has(e, evo.pair(p, s)) and evo.get(e, evo.pair(p, s)) == true) - assert(evo.has(e, evo.pair(s, p)) and evo.get(e, evo.pair(s, p)) == true) - assert(evo.has(e, p) and evo.get(e, p) == true) - end - end -end - -do - do - local p, s = evo.id(2) - - local e = evo.builder():set(p, 21):set(evo.pair(p, s), 42):spawn() - assert(evo.has(e, p) and evo.get(e, p) == 21) - assert(evo.has(e, evo.pair(p, s)) and evo.get(e, evo.pair(p, s)) == 42) - - evo.set(s, evo.TAG) - assert(evo.has(e, p) and evo.get(e, p) == 21) - assert(evo.has(e, evo.pair(p, s)) and evo.get(e, evo.pair(p, s)) == 42) - - evo.set(p, evo.TAG) - assert(evo.has(e, p) and evo.get(e, p) == nil) - assert(evo.has(e, evo.pair(p, s)) and evo.get(e, evo.pair(p, s)) == nil) - end - - do - local p, s = evo.id(2) - - local e = evo.builder():set(evo.pair(p, s), 42):spawn() - assert(evo.has(e, evo.pair(p, s)) and evo.get(e, evo.pair(p, s)) == 42) - - evo.set(s, evo.TAG) - assert(evo.has(e, evo.pair(p, s)) and evo.get(e, evo.pair(p, s)) == 42) - - evo.set(p, evo.TAG) - assert(evo.has(e, evo.pair(p, s)) and evo.get(e, evo.pair(p, s)) == nil) - end -end - -do - local p1, p2, s1, s2 = evo.id(4) - - do - local b = evo.builder() - - b:set(evo.pair(p1, s1), 11) - b:set(evo.pair(p1, s2), 12) - b:set(evo.pair(p2, s1), 21) - b:set(evo.pair(p2, s2), 22) - - b:remove(evo.pair(evo.ANY, evo.ANY)) - - assert(not b:has(evo.pair(p1, s1))) - assert(not b:has(evo.pair(p1, s2))) - assert(not b:has(evo.pair(p2, s1))) - assert(not b:has(evo.pair(p2, s2))) - - assert(not b:has(evo.pair(p1, evo.ANY))) - assert(not b:has(evo.pair(p2, evo.ANY))) - assert(not b:has(evo.pair(evo.ANY, s1))) - assert(not b:has(evo.pair(evo.ANY, s2))) - - assert(not b:has(evo.pair(evo.ANY, evo.ANY))) - end - - do - local b = evo.builder() - - b:set(evo.pair(p1, s1), 11) - b:set(evo.pair(p1, s2), 12) - b:set(evo.pair(p2, s1), 21) - b:set(evo.pair(p2, s2), 22) - - b:remove(evo.pair(p2, evo.ANY)) - - assert(b:has(evo.pair(p1, s1))) - assert(b:has(evo.pair(p1, s2))) - assert(not b:has(evo.pair(p2, s1))) - assert(not b:has(evo.pair(p2, s2))) - - assert(b:has(evo.pair(p1, evo.ANY))) - assert(not b:has(evo.pair(p2, evo.ANY))) - assert(b:has(evo.pair(evo.ANY, s1))) - assert(b:has(evo.pair(evo.ANY, s2))) - - assert(b:has(evo.pair(evo.ANY, evo.ANY))) - end - - do - local b = evo.builder() - - b:set(evo.pair(p1, s1), 11) - b:set(evo.pair(p1, s2), 12) - b:set(evo.pair(p2, s1), 21) - b:set(evo.pair(p2, s2), 22) - - b:remove(evo.pair(p2, evo.ANY)) - - assert(b:has_all(evo.pair(p1, s1))) - assert(b:has_all(evo.pair(p1, s1), evo.pair(p1, s2))) - assert(not b:has_all(evo.pair(p1, s1), evo.pair(p2, s1))) - assert(not b:has_all(evo.pair(p2, s1), evo.pair(p1, s2))) - assert(not b:has_all(evo.pair(p2, s1), evo.pair(p2, s2))) - - assert(b:has_all(evo.pair(p1, evo.ANY))) - assert(b:has_all(evo.pair(p1, evo.ANY), evo.pair(evo.ANY, s1))) - assert(not b:has_all(evo.pair(p2, evo.ANY), evo.pair(evo.ANY, s1))) - assert(not b:has_all(evo.pair(p2, evo.ANY), evo.pair(evo.ANY, p1))) - - assert(b:has_all(evo.pair(evo.ANY, evo.ANY))) - - assert(b:has_all( - evo.pair(p1, s1), - evo.pair(p1, s2), - evo.pair(evo.ANY, s1), - evo.pair(p1, evo.ANY), - evo.pair(evo.ANY, evo.ANY))) - - assert(not b:has_all( - evo.pair(p1, s1), - evo.pair(p1, s2), - evo.pair(evo.ANY, s1), - evo.pair(p1, evo.ANY), - evo.pair(evo.ANY, evo.ANY), - evo.pair(p2, evo.ANY))) - end - - do - local b = evo.builder() - - b:set(evo.pair(p1, s1), 11) - b:set(evo.pair(p1, s2), 12) - b:set(evo.pair(p2, s1), 21) - b:set(evo.pair(p2, s2), 22) - - b:remove(evo.pair(p2, evo.ANY)) - - assert(b:has_any(evo.pair(p1, s1))) - assert(b:has_any(evo.pair(p1, s1), evo.pair(p1, s2))) - assert(b:has_any(evo.pair(p1, s1), evo.pair(p2, s1))) - assert(b:has_any(evo.pair(p2, s1), evo.pair(p1, s2))) - assert(not b:has_any(evo.pair(p2, s1), evo.pair(p2, s2))) - - assert(b:has_any(evo.pair(p1, evo.ANY))) - assert(b:has_any(evo.pair(p1, evo.ANY), evo.pair(evo.ANY, s1))) - assert(b:has_any(evo.pair(p2, evo.ANY), evo.pair(evo.ANY, s1))) - assert(not b:has_any(evo.pair(p2, evo.ANY), evo.pair(evo.ANY, p1))) - - assert(b:has_any(evo.pair(evo.ANY, evo.ANY))) - - assert(b:has_any( - evo.pair(p1, s1), - evo.pair(p1, s2), - evo.pair(evo.ANY, s1), - evo.pair(p1, evo.ANY), - evo.pair(evo.ANY, evo.ANY))) - - assert(not b:has_any( - evo.pair(p2, s1), - evo.pair(p2, s2), - evo.pair(p2, evo.ANY), - evo.pair(evo.ANY, p1), - evo.pair(evo.ANY, p2))) - - assert(b:has_any( - evo.pair(p2, s1), - evo.pair(p2, s2), - evo.pair(p2, evo.ANY), - evo.pair(evo.ANY, p1), - evo.pair(evo.ANY, p2), - evo.pair(p1, evo.ANY))) - end - - do - local b = evo.builder() - - b:set(evo.pair(p1, s1), 11) - b:set(evo.pair(p1, s2), 12) - b:set(evo.pair(p2, s1), 21) - b:set(evo.pair(p2, s2), 22) - - b:remove(evo.pair(p1, evo.ANY)) - b:remove(evo.pair(p1, evo.ANY)) - - b:remove(evo.pair(p2, evo.ANY)) - b:remove(evo.pair(p2, evo.ANY)) - - b:remove(evo.pair(evo.ANY, s1)) - b:remove(evo.pair(evo.ANY, s1)) - - b:remove(evo.pair(evo.ANY, s2)) - b:remove(evo.pair(evo.ANY, s2)) - - assert(not b:has(evo.pair(evo.ANY, evo.ANY))) - end -end - -do - local p, s = evo.id(2) - evo.set(p, evo.NAME, 'p') - evo.set(s, evo.NAME, 's') - assert(evo.name(evo.pair(p, s)) == '${p,s}') - assert(evo.name(evo.pair(evo.ANY, s)) == '${ANY,s}') - assert(evo.name(evo.pair(p, evo.ANY)) == '${p,ANY}') - assert(evo.name(evo.pair(evo.ANY, evo.ANY)) == '${ANY,ANY}') -end - -do - do - local p, s = evo.id(2) - assert(evo.alive(evo.pair(p, s))) - end - - do - local p, s = evo.id(2) - evo.destroy(p) - assert(not evo.alive(evo.pair(p, s))) - end - - do - local p, s = evo.id(2) - evo.destroy(s) - assert(not evo.alive(evo.pair(p, s))) - end - - do - local p, s = evo.id(2) - evo.destroy(p, s) - assert(not evo.alive(evo.pair(p, s))) - end -end - -do - do - local p1, p2, p3, s = evo.id(4) - local prefab = evo.builder() - :set(evo.pair(p1, s), 42) - :set(evo.pair(p2, s), 21) - :set(evo.pair(p3, s), 84) - :spawn() - local clone = evo.clone(prefab) - assert(evo.has(clone, evo.pair(p1, s)) and evo.get(clone, evo.pair(p1, s)) == 42) - assert(evo.has(clone, evo.pair(p2, s)) and evo.get(clone, evo.pair(p2, s)) == 21) - assert(evo.has(clone, evo.pair(p3, s)) and evo.get(clone, evo.pair(p3, s)) == 84) - end - - do - local p1, p2, p3, s = evo.id(4) - evo.set(p1, evo.UNIQUE) - evo.set(p2, evo.UNIQUE) - local prefab = evo.builder() - :set(evo.pair(p1, s), 42) - :set(evo.pair(p2, s), 21) - :set(evo.pair(p3, s), 84) - :spawn() - local clone = evo.clone(prefab) - assert(not evo.has(clone, evo.pair(p1, s)) and evo.get(clone, evo.pair(p1, s)) == nil) - assert(not evo.has(clone, evo.pair(p2, s)) and evo.get(clone, evo.pair(p2, s)) == nil) - assert(evo.has(clone, evo.pair(p3, s)) and evo.get(clone, evo.pair(p3, s)) == 84) - end -end - -do - ---@generic T1, T2 - ---@param first T1 - ---@param second T2 - ---@return T1 - ---@diagnostic disable-next-line: unused-local - local function fst(first, second) - return first - end - - ---@generic T1, T2 - ---@param first T1 - ---@param second T2 - ---@return T2 - ---@diagnostic disable-next-line: unused-local - local function snd(first, second) - return second - end - - do - local p, s1, s2, f = evo.id(4) - - local e = evo.builder() - :set(evo.pair(p, s1), 21) - :set(evo.pair(p, s2), 42) - :spawn() - - assert(fst(evo.primary(e, s1)) == p) - assert(snd(evo.primary(e, s1)) == 21) - assert(fst(evo.primary(e, s2)) == p) - assert(snd(evo.primary(e, s2)) == 42) - assert(fst(evo.primary(e, s1, 1)) == p) - assert(snd(evo.primary(e, s1, 1)) == 21) - assert(fst(evo.primary(e, s2, 1)) == p) - assert(snd(evo.primary(e, s2, 1)) == 42) - assert(fst(evo.primary(e, s1, 2)) == nil) - assert(snd(evo.primary(e, s1, 2)) == nil) - assert(fst(evo.primary(e, s2, 2)) == nil) - assert(snd(evo.primary(e, s2, 2)) == nil) - - assert(fst(evo.primary(evo.pair(e, f), s1)) == nil) - assert(fst(evo.primary(evo.pair(e, f), s2)) == nil) - assert(fst(evo.primary(evo.pair(e, f), s1, 1)) == nil) - assert(fst(evo.primary(evo.pair(e, f), s2, 1)) == nil) - assert(fst(evo.primary(evo.pair(e, f), s1, 2)) == nil) - assert(fst(evo.primary(evo.pair(e, f), s2, 2)) == nil) - - assert(fst(evo.secondary(e, p)) == s1) - assert(fst(evo.secondary(e, p, 1)) == s1) - assert(fst(evo.secondary(e, p, 2)) == s2) - assert(fst(evo.secondary(e, p, 3)) == nil) - - assert(fst(evo.secondary(evo.pair(e, f), p)) == nil) - assert(fst(evo.secondary(evo.pair(e, f), p, 1)) == nil) - assert(fst(evo.secondary(evo.pair(e, f), p, 2)) == nil) - assert(fst(evo.secondary(evo.pair(e, f), p, 3)) == nil) - - assert(fst(evo.primary(evo.pair(f, e), s1)) == nil) - assert(fst(evo.primary(evo.pair(f, e), s2)) == nil) - assert(fst(evo.secondary(evo.pair(f, e), p)) == nil) - end - - do - local p, s1, s2, f = evo.id(4) - - local e = evo.builder() - :set(evo.pair(p, s1), 21) - :set(evo.pair(p, s2), 42) - :spawn() - - assert(evo.primary_count(e, p) == 0) - assert(evo.primary_count(e, s1) == 1) - assert(evo.primary_count(e, s2) == 1) - assert(evo.secondary_count(e, p) == 2) - assert(evo.secondary_count(e, s1) == 0) - assert(evo.secondary_count(e, s2) == 0) - - assert(evo.primary_count(evo.pair(e, f), p) == 0) - assert(evo.primary_count(evo.pair(e, f), s1) == 0) - assert(evo.primary_count(evo.pair(e, f), s2) == 0) - assert(evo.secondary_count(evo.pair(e, f), p) == 0) - assert(evo.secondary_count(evo.pair(e, f), s1) == 0) - assert(evo.secondary_count(evo.pair(e, f), s2) == 0) - - assert(evo.primary_count(evo.pair(f, e), p) == 0) - assert(evo.primary_count(evo.pair(f, e), s1) == 0) - assert(evo.primary_count(evo.pair(f, e), s2) == 0) - assert(evo.secondary_count(evo.pair(f, e), p) == 0) - assert(evo.secondary_count(evo.pair(f, e), s1) == 0) - assert(evo.secondary_count(evo.pair(f, e), s2) == 0) - end - - do - local p, s1, s2, f = evo.id(4) - - local e = evo.builder() - :set(evo.pair(p, s1), 21) - :set(evo.pair(p, s2), 42) - :spawn() - - do - local iter, state = evo.primaries(e, p) - local fragment, component = iter(state) - assert(fragment == nil and component == nil) - - - iter, state = evo.primaries(evo.pair(e, f), p) - fragment, component = iter(state) - assert(fragment == nil and component == nil) - end - - do - local iter, state = evo.primaries(e, s1) - local fragment, component = iter(state) - assert(fragment == p and component == 21) - fragment, component = iter(state) - assert(fragment == nil and component == nil) - - iter, state = evo.primaries(evo.pair(e, f), s1) - fragment, component = iter(state) - assert(fragment == nil and component == nil) - end - - do - local iter, state = evo.primaries(e, s2) - local fragment, component = iter(state) - assert(fragment == p and component == 42) - fragment, component = iter(state) - assert(fragment == nil and component == nil) - - iter, state = evo.primaries(evo.pair(e, f), s2) - fragment, component = iter(state) - assert(fragment == nil and component == nil) - end - end - - do - local p, s1, s2, f = evo.id(4) - - local e = evo.builder() - :set(evo.pair(p, s1), 21) - :set(evo.pair(p, s2), 42) - :spawn() - - do - local iter, state = evo.secondaries(e, s1) - local fragment, component = iter(state) - assert(fragment == nil and component == nil) - - iter, state = evo.secondaries(evo.pair(e, f), s1) - fragment, component = iter(state) - assert(fragment == nil and component == nil) - end - - do - local iter, state = evo.secondaries(e, p) - local fragment, component = iter(state) - assert(fragment == s1 and component == 21) - fragment, component = iter(state) - assert(fragment == s2 and component == 42) - fragment, component = iter(state) - assert(fragment == nil and component == nil) - - iter, state = evo.secondaries(evo.pair(e, f), p) - fragment, component = iter(state) - assert(fragment == nil and component == nil) - end - end - - do - local p, s1, s2 = evo.id(3) - - local e = evo.builder() - :set(evo.pair(p, s1), 21) - :set(evo.pair(p, s2), 42) - :spawn() - - do - local iter, state = evo.each(e) - local fragment, component = iter(state) - assert(fragment == evo.pair(p, s1) and component == 21) - fragment, component = iter(state) - assert(fragment == evo.pair(p, s2) and component == 42) - fragment, component = iter(state) - assert(fragment == nil and component == nil) - end - end -end - -do - do - local p, s1, s2 = evo.id(3) - - local e = evo.spawn { - [evo.pair(p, s1)] = 21, - [evo.pair(p, s2)] = 42, - } - - assert(evo.has(e, evo.pair(p, s1)) and evo.get(e, evo.pair(p, s1)) == 21) - assert(evo.has(e, evo.pair(p, s2)) and evo.get(e, evo.pair(p, s2)) == 42) - - evo.destroy(p) - - assert(not evo.has(e, evo.pair(p, s1)) and evo.get(e, evo.pair(p, s1)) == nil) - assert(not evo.has(e, evo.pair(p, s2)) and evo.get(e, evo.pair(p, s2)) == nil) - end - - do - local p, s1, s2 = evo.id(3) - - local e = evo.spawn { - [evo.pair(p, s1)] = 21, - [evo.pair(p, s2)] = 42, - } - - assert(evo.has(e, evo.pair(p, s1)) and evo.get(e, evo.pair(p, s1)) == 21) - assert(evo.has(e, evo.pair(p, s2)) and evo.get(e, evo.pair(p, s2)) == 42) - - evo.destroy(s1) - - assert(not evo.has(e, evo.pair(p, s1)) and evo.get(e, evo.pair(p, s1)) == nil) - assert(evo.has(e, evo.pair(p, s2)) and evo.get(e, evo.pair(p, s2)) == 42) - - evo.destroy(s2) - - assert(not evo.has(e, evo.pair(p, s1)) and evo.get(e, evo.pair(p, s1)) == nil) - assert(not evo.has(e, evo.pair(p, s2)) and evo.get(e, evo.pair(p, s2)) == nil) - end -end - --- TODO --- process evo.ANY as single wildcard --- should we provide an evolved.pair type? --- we should not add any wildcards to chunk `with` edges, lol --- edges `without` to nil don't work because they are cannot be found diff --git a/evolved.lua b/evolved.lua index 9fc3ec7..636293a 100644 --- a/evolved.lua +++ b/evolved.lua @@ -28,7 +28,6 @@ local evolved = { } ---@class evolved.id ----@alias evolved.pair evolved.id ---@alias evolved.entity evolved.id ---@alias evolved.fragment evolved.id @@ -83,20 +82,6 @@ local evolved = { ---@field package [3] integer chunk_stack_size ---@field package [4] table? exclude_set ----@class (exact) evolved.primaries_state ----@field package [1] integer structural_changes ----@field package [2] evolved.chunk entity_chunk ----@field package [3] integer entity_place ----@field package [4] integer secondary_index ----@field package [5] integer secondary_fragment_index - ----@class (exact) evolved.secondaries_state ----@field package [1] integer structural_changes ----@field package [2] evolved.chunk entity_chunk ----@field package [3] integer entity_place ----@field package [4] integer primary_index ----@field package [5] integer primary_fragment_index - ---@alias evolved.each_iterator fun( --- state: evolved.each_state?): --- evolved.fragment?, evolved.component? @@ -105,14 +90,6 @@ local evolved = { --- state: evolved.execute_state?): --- evolved.chunk?, evolved.entity[]?, integer? ----@alias evolved.primaries_iterator fun( ---- state: evolved.primaries_state?): ---- evolved.fragment?, evolved.component? - ----@alias evolved.secondaries_iterator fun( ---- state: evolved.secondaries_state?): ---- evolved.fragment?, evolved.component? - --- --- --- @@ -121,22 +98,13 @@ local evolved = { --[=[------------------------------------------------------------------\ | |-------- OPTIONS --------|- SECONDARY -|-- PRIMARY --| - | IDENTIFIER'S | 12 bits | | | - | ANATOMY |--------|--------|-------| 20 bits | 20 bits | - | | 9 bits | 2 bits | 1 bit | | | - |--------------|--------|--------|-------|-------------|-------------| - | ID | RSVD | 00 | 0 | version | index | - | PAIR | RSVD | 00 | 1 | SEC index | PRI index | - | PRI WILDCARD | RSVD | 01 | 1 | SEC index | ANY index | - | SEC WILDCARD | RSVD | 10 | 1 | ANY index | PRI index | - | ANY WILDCARD | RSVD | 11 | 1 | ANY index | ANY index | + | IDENTIFIER'S | | | | + | ANATOMY | 12 bits | 20 bits | 20 bits | + | | | | | + |--------------|-------------------------|-------------|-------------| + | ID | RESERVED | version | index | \------------------------------------------------------------------]=] -local __PAIR_OPTIONS = 1 -- 0b001 -local __PRI_WILDCARD_OPTIONS = 3 -- 0b011 -local __SEC_WILDCARD_OPTIONS = 5 -- 0b101 -local __ANY_WILDCARD_OPTIONS = 7 -- 0b111 - --- --- --- @@ -191,10 +159,6 @@ local __group_subsystems = {} ---@type table ---@field package __component_storages evolved.storage[] ---@field package __component_fragments evolved.fragment[] ----@field package __pair_list evolved.pair[] ----@field package __pair_count integer ----@field package __primary_pairs table> ----@field package __secondary_pairs table> ---@field package __with_fragment_edges table ---@field package __without_fragment_edges table ---@field package __unreachable_or_collected boolean @@ -202,9 +166,6 @@ local __group_subsystems = {} ---@type table ----@field package __primary_pairs? table> ----@field package __secondary_pairs? table> local __builder_mt = {} __builder_mt.__index = __builder_mt @@ -467,15 +426,13 @@ local __table_pool_tag = { system_list = 3, each_state = 4, execute_state = 5, - primaries_state = 6, - secondaries_state = 7, - entity_set = 8, - entity_list = 9, - fragment_set = 10, - fragment_list = 11, - component_map = 12, - component_list = 13, - __count = 13, + entity_set = 6, + entity_list = 7, + fragment_set = 8, + fragment_list = 9, + component_map = 10, + component_list = 11, + __count = 11, } ---@class (exact) evolved.table_pool @@ -584,7 +541,6 @@ end --- } local __assoc_list_new -local __assoc_list_dup local __assoc_list_move local __assoc_list_move_ex local __assoc_list_sort @@ -606,31 +562,6 @@ function __assoc_list_new(reserve) } end ----@generic K ----@param al evolved.assoc_list ----@return evolved.assoc_list ----@nodiscard -function __assoc_list_dup(al) - local al_item_list = al.__item_list - local al_item_count = al.__item_count - - local dup_item_set = __lua_table_new(0, al_item_count) - local dup_item_list = __lua_table_new(al_item_count, 0) - - for al_item_index = 1, al_item_count do - local al_item = al_item_list[al_item_index] - dup_item_set[al_item] = al_item_index - dup_item_list[al_item_index] = al_item - end - - ---@type evolved.assoc_list - return { - __item_set = dup_item_set, - __item_list = dup_item_list, - __item_count = al_item_count, - } -end - ---@generic K ---@param src_item_list K[] ---@param src_item_first integer @@ -780,8 +711,6 @@ end --- --- -local __ANY = __acquire_id() - local __TAG = __acquire_id() local __NAME = __acquire_id() @@ -822,18 +751,6 @@ local __DESTRUCTION_POLICY_REMOVE_FRAGMENT = __acquire_id() --- --- -local __ANY_INDEX = __ANY % 2 ^ 20 --[[@as integer]] - -local __ANY_WILDCARD = __ANY_INDEX - + __ANY_INDEX * 2 ^ 20 - + __ANY_WILDCARD_OPTIONS * 2 ^ 40 --[[@as evolved.pair]] - ---- ---- ---- ---- ---- - local __safe_tbls = { __EMPTY_FRAGMENT_SET = __lua_setmetatable({}, { __tostring = function() return 'empty fragment set' end, @@ -911,21 +828,6 @@ local __evolved_process local __evolved_debug_mode local __evolved_collect_garbage -local __evolved_pair -local __evolved_unpair - -local __evolved_is_pair -local __evolved_is_wildcard - -local __evolved_primary -local __evolved_secondary - -local __evolved_primaries -local __evolved_secondaries - -local __evolved_primary_count -local __evolved_secondary_count - local __evolved_chunk local __evolved_builder @@ -935,14 +837,6 @@ local __evolved_builder --- --- -local __primary_has -local __primary_has_all -local __primary_has_any -local __primary_get - -local __primary_wildcard -local __secondary_wildcard - local __universal_name local __component_storage @@ -971,186 +865,20 @@ local __fragment_required_fragments --- --- ----@param id evolved.id | evolved.pair ----@param fragment evolved.fragment ----@return boolean ----@nodiscard -function __primary_has(id, fragment) - local id_primary, _, id_options = __evolved_unpack(id) - - if id_options < __PAIR_OPTIONS then - if __freelist_ids[id_primary] ~= id then - return false - end - else - local id_primary_id = __freelist_ids[id_primary] --[[@as evolved.id?]] - if not id_primary_id or id_primary_id % 2 ^ 20 ~= id_primary then - return false - end - end - - local id_chunk = __entity_chunks[id_primary] - - return id_chunk and __chunk_has_fragment(id_chunk, fragment) or false -end - ----@param id evolved.id | evolved.pair ----@param ... evolved.fragment fragments ----@return boolean ----@nodiscard -function __primary_has_all(id, ...) - local argument_count = select('#', ...) - - if argument_count == 0 then - return true - end - - local id_primary, _, id_options = __evolved_unpack(id) - - if id_options < __PAIR_OPTIONS then - if __freelist_ids[id_primary] ~= id then - return false - end - else - local id_primary_id = __freelist_ids[id_primary] --[[@as evolved.id?]] - if not id_primary_id or id_primary_id % 2 ^ 20 ~= id_primary then - return false - end - end - - local id_chunk = __entity_chunks[id_primary] - - return id_chunk and __chunk_has_all_fragments(id_chunk, ...) or false -end - ----@param id evolved.id | evolved.pair ----@param ... evolved.fragment fragments ----@return boolean ----@nodiscard -function __primary_has_any(id, ...) - local argument_count = select('#', ...) - - if argument_count == 0 then - return false - end - - local id_primary, _, id_options = __evolved_unpack(id) - - if id_options < __PAIR_OPTIONS then - if __freelist_ids[id_primary] ~= id then - return false - end - else - local id_primary_id = __freelist_ids[id_primary] --[[@as evolved.id?]] - if not id_primary_id or id_primary_id % 2 ^ 20 ~= id_primary then - return false - end - end - - local id_chunk = __entity_chunks[id_primary] - - return id_chunk and __chunk_has_any_fragments(id_chunk, ...) or false -end - ----@param id evolved.id | evolved.pair ----@param ... evolved.fragment fragments ----@return evolved.component ... components ----@nodiscard -function __primary_get(id, ...) - local fragment_count = select('#', ...) - - if fragment_count == 0 then - return - end - - local id_primary, _, id_options = __evolved_unpack(id) - - if id_options < __PAIR_OPTIONS then - if __freelist_ids[id_primary] ~= id then - return - end - else - local id_primary_id = __freelist_ids[id_primary] --[[@as evolved.id?]] - if not id_primary_id or id_primary_id % 2 ^ 20 ~= id_primary then - return - end - end - - local id_chunk = __entity_chunks[id_primary] - - if not id_chunk then - return - end - - local id_place = __entity_places[id_primary] - return __chunk_get_components(id_chunk, id_place, ...) -end - ----@param secondary evolved.id | integer id or index ----@return evolved.pair (*, secondary) ----@nodiscard -function __primary_wildcard(secondary) - local primary_index = __ANY_INDEX - local secondary_index = secondary % 2 ^ 20 - - if secondary_index == __ANY_INDEX then - return __ANY_WILDCARD - end - - return primary_index - + secondary_index * 2 ^ 20 - + __PRI_WILDCARD_OPTIONS * 2 ^ 40 --[[@as evolved.pair]] -end - ----@param primary evolved.id | integer id or index ----@return evolved.pair (primary, *) ----@nodiscard -function __secondary_wildcard(primary) - local primary_index = primary % 2 ^ 20 - local secondary_index = __ANY_INDEX - - if primary_index == __ANY_INDEX then - return __ANY_WILDCARD - end - - return primary_index - + secondary_index * 2 ^ 20 - + __SEC_WILDCARD_OPTIONS * 2 ^ 40 --[[@as evolved.pair]] -end - ---@param id evolved.id ---@return string ---@nodiscard function __universal_name(id) - local id_primary, id_secondary, id_options = __evolved_unpack(id) + local id_primary, id_secondary = __evolved_unpack(id) - if id_options < __PAIR_OPTIONS then - ---@type string? - local id_name = __evolved_get(id, __NAME) + ---@type string? + local id_name = __evolved_get(id, __NAME) - if id_name then - return id_name - end - else - ---@type string?, string? - local pair_primary_id_name, pair_secondary_id_name - - local pair_primary_id = __freelist_ids[id_primary] --[[@as evolved.id?]] - if pair_primary_id and pair_primary_id % 2 ^ 20 == id_primary then - pair_primary_id_name = __universal_name(pair_primary_id) - end - - local pair_secondary_id = __freelist_ids[id_secondary] --[[@as evolved.id?]] - if pair_secondary_id and pair_secondary_id % 2 ^ 20 == id_secondary then - pair_secondary_id_name = __universal_name(pair_secondary_id) - end - - if pair_primary_id_name and pair_secondary_id_name then - return __lua_string_format('${%s,%s}', pair_primary_id_name, pair_secondary_id_name) - end + if id_name then + return id_name end - return __lua_string_format('$%d#%d:%d:%d', id, id_primary, id_secondary, id_options) + return __lua_string_format('$%d#%d:%d', id, id_primary, id_secondary) end ---@param fragment evolved.fragment @@ -1199,7 +927,7 @@ function __iterator_fns.__each_iterator(each_state) end ---@type evolved.execute_iterator -function __iterator_fns.__execute_major_iterator(execute_state) +function __iterator_fns.__execute_iterator(execute_state) if not execute_state then return end local structural_changes = execute_state[1] @@ -1228,16 +956,6 @@ function __iterator_fns.__execute_major_iterator(execute_state) (not chunk_child.__has_explicit_major) and (not exclude_set or not exclude_set[chunk_child_fragment]) - if is_chunk_child_matched and exclude_set and chunk_child.__has_pair_major then - local chunk_child_fragment_primary, chunk_child_fragment_secondary = - __evolved_unpack(chunk_child_fragment) - - is_chunk_child_matched = - not exclude_set[__ANY_WILDCARD] and - not exclude_set[__primary_wildcard(chunk_child_fragment_secondary)] and - not exclude_set[__secondary_wildcard(chunk_child_fragment_primary)] - end - if is_chunk_child_matched then chunk_stack_size = chunk_stack_size + 1 chunk_stack[chunk_stack_size] = chunk_child @@ -1257,103 +975,6 @@ function __iterator_fns.__execute_major_iterator(execute_state) __release_table(__table_pool_tag.execute_state, execute_state, true) end ----@type evolved.execute_iterator -function __iterator_fns.__execute_minor_iterator(execute_state) - if not execute_state then return end - - local structural_changes = execute_state[1] - local chunk_stack = execute_state[2] - local chunk_stack_size = execute_state[3] - - if structural_changes ~= __structural_changes then - __error_fmt('structural changes are prohibited during iteration') - end - - while chunk_stack_size > 0 do - local chunk = chunk_stack[chunk_stack_size] - - chunk_stack[chunk_stack_size] = nil - chunk_stack_size = chunk_stack_size - 1 - - local chunk_entity_list = chunk.__entity_list - local chunk_entity_count = chunk.__entity_count - - if chunk_entity_count > 0 then - execute_state[3] = chunk_stack_size - return chunk, chunk_entity_list, chunk_entity_count - end - end - - __release_table(__table_pool_tag.chunk_list, chunk_stack, true) - __release_table(__table_pool_tag.execute_state, execute_state, true) -end - ----@type evolved.primaries_iterator -function __iterator_fns.__primaries_iterator(primaries_state) - if not primaries_state then return end - - local structural_changes = primaries_state[1] - local entity_chunk = primaries_state[2] - local entity_place = primaries_state[3] - local secondary_index = primaries_state[4] - local secondary_fragment_index = primaries_state[5] - - if structural_changes ~= __structural_changes then - __error_fmt('structural changes are prohibited during iteration') - end - - local secondary_fragments = entity_chunk.__secondary_pairs[secondary_index] - local secondary_fragment_list = secondary_fragments and secondary_fragments.__item_list - local secondary_fragment_count = secondary_fragments and secondary_fragments.__item_count or 0 - - if secondary_fragment_index >= 1 and secondary_fragment_index <= secondary_fragment_count then - primaries_state[5] = secondary_fragment_index + 1 - - local secondary_fragment = secondary_fragment_list[secondary_fragment_index] - local primary, _ = __evolved_unpair(secondary_fragment) - - local component_index = entity_chunk.__component_indices[secondary_fragment] - local component_storage = entity_chunk.__component_storages[component_index] - - return primary, component_storage and component_storage[entity_place] - end - - __release_table(__table_pool_tag.primaries_state, primaries_state, true) -end - ----@type evolved.secondaries_iterator -function __iterator_fns.__secondaries_iterator(secondaries_state) - if not secondaries_state then return end - - local structural_changes = secondaries_state[1] - local entity_chunk = secondaries_state[2] - local entity_place = secondaries_state[3] - local primary_index = secondaries_state[4] - local primary_fragment_index = secondaries_state[5] - - if structural_changes ~= __structural_changes then - __error_fmt('structural changes are prohibited during iteration') - end - - local primary_fragments = entity_chunk.__primary_pairs[primary_index] - local primary_fragment_list = primary_fragments and primary_fragments.__item_list - local primary_fragment_count = primary_fragments and primary_fragments.__item_count or 0 - - if primary_fragment_index >= 1 and primary_fragment_index <= primary_fragment_count then - secondaries_state[5] = primary_fragment_index + 1 - - local primary_fragment = primary_fragment_list[primary_fragment_index] - local _, secondary = __evolved_unpair(primary_fragment) - - local component_index = entity_chunk.__component_indices[primary_fragment] - local component_storage = entity_chunk.__component_storages[component_index] - - return secondary, component_storage and component_storage[entity_place] - end - - __release_table(__table_pool_tag.secondaries_state, secondaries_state, true) -end - --- --- --- @@ -1373,74 +994,24 @@ local __update_major_chunks_trace ---@return evolved.chunk ---@nodiscard function __new_chunk(chunk_parent, chunk_fragment) - local chunk_fragment_primary, chunk_fragment_secondary, chunk_fragment_options = - __evolved_unpack(chunk_fragment) + local chunk_fragment_primary, _ = __evolved_unpack(chunk_fragment) - if chunk_fragment_options < __PAIR_OPTIONS then - if chunk_fragment_primary == __ANY_INDEX then - __error_fmt('the id (%s) is a wildcard and cannot be used for a new chunk', - __universal_name(chunk_fragment)) - elseif __freelist_ids[chunk_fragment_primary] ~= chunk_fragment then - __error_fmt('the id (%s) is not alive and cannot be used for a new chunk', - __universal_name(chunk_fragment)) - end - else - if chunk_fragment_options >= __PRI_WILDCARD_OPTIONS then - __error_fmt('the pair (%s) is a wildcard and cannot be used for a new chunk', - __universal_name(chunk_fragment)) - end - - local fragment_primary_id = __freelist_ids[chunk_fragment_primary] --[[@as evolved.id?]] - if not fragment_primary_id or fragment_primary_id % 2 ^ 20 ~= chunk_fragment_primary then - __error_fmt('the pair (%s) has no alive primary id and cannot be used for a new chunk', - __universal_name(chunk_fragment)) - end - - local fragment_secondary_id = __freelist_ids[chunk_fragment_secondary] --[[@as evolved.id?]] - if not fragment_secondary_id or fragment_secondary_id % 2 ^ 20 ~= chunk_fragment_secondary then - __error_fmt('the pair (%s) has no alive secondary id and cannot be used for a new chunk', - __universal_name(chunk_fragment)) - end + if __freelist_ids[chunk_fragment_primary] ~= chunk_fragment then + __error_fmt('the id (%s) is not alive and cannot be used for a new chunk', + __universal_name(chunk_fragment)) end local chunk_fragment_set = {} ---@type table local chunk_fragment_list = {} ---@type evolved.fragment[] local chunk_fragment_count = 0 ---@type integer - local chunk_pair_list = {} ---@type evolved.pair[] - local chunk_pair_count = 0 ---@type integer - - local chunk_primary_pairs = {} ---@type table> - local chunk_secondary_pairs = {} ---@type table> - if chunk_parent then - do - local chunk_parent_fragment_list = chunk_parent.__fragment_list - local chunk_parent_fragment_count = chunk_parent.__fragment_count + local chunk_parent_fragment_list = chunk_parent.__fragment_list + local chunk_parent_fragment_count = chunk_parent.__fragment_count - chunk_fragment_count = __assoc_list_move_ex( - chunk_parent_fragment_list, 1, chunk_parent_fragment_count, - chunk_fragment_set, chunk_fragment_list, chunk_fragment_count) - end - - do - local chunk_parent_pair_list = chunk_parent.__pair_list - local chunk_parent_pair_count = chunk_parent.__pair_count - - __lua_table_move( - chunk_parent_pair_list, 1, chunk_parent_pair_count, - chunk_pair_count + 1, chunk_pair_list) - - chunk_pair_count = chunk_pair_count + chunk_parent_pair_count - end - - for parent_primary_index, parent_primary_fragments in __lua_next, chunk_parent.__primary_pairs do - chunk_primary_pairs[parent_primary_index] = __assoc_list_dup(parent_primary_fragments) - end - - for parent_secondary_index, parent_secondary_fragments in __lua_next, chunk_parent.__secondary_pairs do - chunk_secondary_pairs[parent_secondary_index] = __assoc_list_dup(parent_secondary_fragments) - end + chunk_fragment_count = __assoc_list_move_ex( + chunk_parent_fragment_list, 1, chunk_parent_fragment_count, + chunk_fragment_set, chunk_fragment_list, chunk_fragment_count) end do @@ -1449,29 +1020,6 @@ function __new_chunk(chunk_parent, chunk_fragment) chunk_fragment_list[chunk_fragment_count] = chunk_fragment end - if chunk_fragment >= __PAIR_OPTIONS * 2 ^ 40 then - chunk_pair_count = chunk_pair_count + 1 - chunk_pair_list[chunk_pair_count] = chunk_fragment - - local chunk_primary_fragments = chunk_primary_pairs[chunk_fragment_primary] - local chunk_secondary_fragments = chunk_secondary_pairs[chunk_fragment_secondary] - - if not chunk_primary_fragments then - ---@type evolved.assoc_list - chunk_primary_fragments = __assoc_list_new(1) - chunk_primary_pairs[chunk_fragment_primary] = chunk_primary_fragments - end - - if not chunk_secondary_fragments then - ---@type evolved.assoc_list - chunk_secondary_fragments = __assoc_list_new(1) - chunk_secondary_pairs[chunk_fragment_secondary] = chunk_secondary_fragments - end - - __assoc_list_insert(chunk_primary_fragments, chunk_fragment) - __assoc_list_insert(chunk_secondary_fragments, chunk_fragment) - end - ---@type evolved.chunk local chunk = __lua_setmetatable({ __parent = nil, @@ -1488,10 +1036,6 @@ function __new_chunk(chunk_parent, chunk_fragment) __component_indices = {}, __component_storages = {}, __component_fragments = {}, - __pair_list = chunk_pair_list, - __pair_count = chunk_pair_count, - __primary_pairs = chunk_primary_pairs, - __secondary_pairs = chunk_secondary_pairs, __with_fragment_edges = {}, __without_fragment_edges = {}, __unreachable_or_collected = false, @@ -1499,9 +1043,6 @@ function __new_chunk(chunk_parent, chunk_fragment) __has_assign_hooks = false, __has_insert_hooks = false, __has_remove_hooks = false, - __has_pair_major = false, - __has_pair_minors = false, - __has_pair_fragments = false, __has_unique_major = false, __has_unique_minors = false, __has_unique_fragments = false, @@ -1559,94 +1100,6 @@ function __new_chunk(chunk_parent, chunk_fragment) __assoc_list_insert(minor_chunks, chunk) end - if chunk_fragment >= __PAIR_OPTIONS * 2 ^ 40 then - local major = chunk_fragment - local major_primary, major_secondary = __evolved_unpack(major) - - do - local major_wildcard = __ANY_WILDCARD - local major_wildcard_chunks = __major_chunks[major_wildcard] - - if not major_wildcard_chunks then - ---@type evolved.assoc_list - major_wildcard_chunks = __assoc_list_new(4) - __major_chunks[major_wildcard] = major_wildcard_chunks - end - - __assoc_list_insert(major_wildcard_chunks, chunk) - end - - do - local major_wildcard = __secondary_wildcard(major_primary) - local major_wildcard_chunks = __major_chunks[major_wildcard] - - if not major_wildcard_chunks then - ---@type evolved.assoc_list - major_wildcard_chunks = __assoc_list_new(4) - __major_chunks[major_wildcard] = major_wildcard_chunks - end - - __assoc_list_insert(major_wildcard_chunks, chunk) - end - - do - local major_wildcard = __primary_wildcard(major_secondary) - local major_wildcard_chunks = __major_chunks[major_wildcard] - - if not major_wildcard_chunks then - ---@type evolved.assoc_list - major_wildcard_chunks = __assoc_list_new(4) - __major_chunks[major_wildcard] = major_wildcard_chunks - end - - __assoc_list_insert(major_wildcard_chunks, chunk) - end - end - - if chunk_pair_count > 0 then - local minor_wildcard = __ANY_WILDCARD - local minor_wildcard_chunks = __minor_chunks[minor_wildcard] - - if not minor_wildcard_chunks then - ---@type evolved.assoc_list - minor_wildcard_chunks = __assoc_list_new(4) - __minor_chunks[minor_wildcard] = minor_wildcard_chunks - end - - __assoc_list_insert(minor_wildcard_chunks, chunk) - end - - for i = 1, chunk_pair_count do - local minor = chunk_pair_list[i] - local minor_primary, minor_secondary = __evolved_unpack(minor) - - do - local minor_wildcard = __secondary_wildcard(minor_primary) - local minor_wildcard_chunks = __minor_chunks[minor_wildcard] - - if not minor_wildcard_chunks then - ---@type evolved.assoc_list - minor_wildcard_chunks = __assoc_list_new(4) - __minor_chunks[minor_wildcard] = minor_wildcard_chunks - end - - __assoc_list_insert(minor_wildcard_chunks, chunk) - end - - do - local minor_wildcard = __primary_wildcard(minor_secondary) - local minor_wildcard_chunks = __minor_chunks[minor_wildcard] - - if not minor_wildcard_chunks then - ---@type evolved.assoc_list - minor_wildcard_chunks = __assoc_list_new(4) - __minor_chunks[minor_wildcard] = minor_wildcard_chunks - end - - __assoc_list_insert(minor_wildcard_chunks, chunk) - end - end - __update_chunk_tags(chunk) __update_chunk_flags(chunk) @@ -1667,7 +1120,7 @@ function __update_chunk_tags(chunk) local fragment = fragment_list[i] local component_index = component_indices[fragment] - if component_index and __primary_has(fragment, __TAG) then + if component_index and __evolved_has(fragment, __TAG) then if component_index ~= component_count then local last_component_storage = component_storages[component_count] local last_component_fragment = component_fragments[component_count] @@ -1684,7 +1137,7 @@ function __update_chunk_tags(chunk) chunk.__component_count = component_count end - if not component_index and not __primary_has(fragment, __TAG) then + if not component_index and not __evolved_has(fragment, __TAG) then component_count = component_count + 1 chunk.__component_count = component_count @@ -1697,7 +1150,7 @@ function __update_chunk_tags(chunk) ---@type evolved.default?, evolved.duplicate? local fragment_default, fragment_duplicate = - __primary_get(fragment, __DEFAULT, __DUPLICATE) + __evolved_get(fragment, __DEFAULT, __DUPLICATE) if fragment_duplicate then for place = 1, chunk.__entity_count do @@ -1723,45 +1176,37 @@ function __update_chunk_flags(chunk) local chunk_fragment = chunk.__fragment local has_setup_hooks = (chunk_parent ~= nil and chunk_parent.__has_setup_hooks) - or __primary_has_any(chunk_fragment, __DEFAULT, __DUPLICATE) + or __evolved_has_any(chunk_fragment, __DEFAULT, __DUPLICATE) local has_assign_hooks = (chunk_parent ~= nil and chunk_parent.__has_assign_hooks) - or __primary_has_any(chunk_fragment, __ON_SET, __ON_ASSIGN) + or __evolved_has_any(chunk_fragment, __ON_SET, __ON_ASSIGN) local has_insert_hooks = (chunk_parent ~= nil and chunk_parent.__has_insert_hooks) - or __primary_has_any(chunk_fragment, __ON_SET, __ON_INSERT) + or __evolved_has_any(chunk_fragment, __ON_SET, __ON_INSERT) local has_remove_hooks = (chunk_parent ~= nil and chunk_parent.__has_remove_hooks) - or __primary_has(chunk_fragment, __ON_REMOVE) + or __evolved_has(chunk_fragment, __ON_REMOVE) - local has_pair_major = chunk_fragment >= __PAIR_OPTIONS * 2 ^ 40 - local has_pair_minors = chunk_parent ~= nil and chunk_parent.__has_pair_fragments - local has_pair_fragments = has_pair_major or has_pair_minors - - local has_unique_major = __primary_has(chunk_fragment, __UNIQUE) + local has_unique_major = __evolved_has(chunk_fragment, __UNIQUE) local has_unique_minors = chunk_parent ~= nil and chunk_parent.__has_unique_fragments local has_unique_fragments = has_unique_major or has_unique_minors - local has_explicit_major = __primary_has(chunk_fragment, __EXPLICIT) + local has_explicit_major = __evolved_has(chunk_fragment, __EXPLICIT) local has_explicit_minors = chunk_parent ~= nil and chunk_parent.__has_explicit_fragments local has_explicit_fragments = has_explicit_major or has_explicit_minors - local has_internal_major = __primary_has(chunk_fragment, __INTERNAL) + local has_internal_major = __evolved_has(chunk_fragment, __INTERNAL) local has_internal_minors = chunk_parent ~= nil and chunk_parent.__has_internal_fragments local has_internal_fragments = has_internal_major or has_internal_minors local has_required_fragments = (chunk_parent ~= nil and chunk_parent.__has_required_fragments) - or __primary_has(chunk_fragment, __REQUIRES) + or __evolved_has(chunk_fragment, __REQUIRES) chunk.__has_setup_hooks = has_setup_hooks chunk.__has_assign_hooks = has_assign_hooks chunk.__has_insert_hooks = has_insert_hooks chunk.__has_remove_hooks = has_remove_hooks - chunk.__has_pair_major = has_pair_major - chunk.__has_pair_minors = has_pair_minors - chunk.__has_pair_fragments = has_pair_fragments - chunk.__has_unique_major = has_unique_major chunk.__has_unique_minors = has_unique_minors chunk.__has_unique_fragments = has_unique_fragments @@ -1781,10 +1226,6 @@ end ---@param trace fun(chunk: evolved.chunk, ...: any) ---@param ... any additional trace arguments function __trace_major_chunks(major, trace, ...) - if major >= __PAIR_OPTIONS * 2 ^ 40 then - __error_fmt('trace operations on pair fragments are not supported') - end - ---@type evolved.chunk[] local chunk_stack = __acquire_table(__table_pool_tag.chunk_list) local chunk_stack_size = 0 @@ -1803,34 +1244,6 @@ function __trace_major_chunks(major, trace, ...) end end - do - local major_chunks = __major_chunks[__primary_wildcard(major)] - local major_chunk_list = major_chunks and major_chunks.__item_list - local major_chunk_count = major_chunks and major_chunks.__item_count or 0 - - if major_chunk_count > 0 then - __lua_table_move( - major_chunk_list, 1, major_chunk_count, - chunk_stack_size + 1, chunk_stack) - - chunk_stack_size = chunk_stack_size + major_chunk_count - end - end - - do - local major_chunks = __major_chunks[__secondary_wildcard(major)] - local major_chunk_list = major_chunks and major_chunks.__item_list - local major_chunk_count = major_chunks and major_chunks.__item_count or 0 - - if major_chunk_count > 0 then - __lua_table_move( - major_chunk_list, 1, major_chunk_count, - chunk_stack_size + 1, chunk_stack) - - chunk_stack_size = chunk_stack_size + major_chunk_count - end - end - while chunk_stack_size > 0 do local chunk = chunk_stack[chunk_stack_size] @@ -1858,10 +1271,6 @@ end ---@param trace fun(chunk: evolved.chunk, ...: any) ---@param ... any additional trace arguments function __trace_minor_chunks(minor, trace, ...) - if minor >= __PAIR_OPTIONS * 2 ^ 40 then - __error_fmt('trace operations on pair fragments are not supported') - end - ---@type evolved.chunk[] local chunk_stack = __acquire_table(__table_pool_tag.chunk_list) local chunk_stack_size = 0 @@ -1880,34 +1289,6 @@ function __trace_minor_chunks(minor, trace, ...) end end - do - local minor_chunks = __minor_chunks[__primary_wildcard(minor)] - local minor_chunk_list = minor_chunks and minor_chunks.__item_list - local minor_chunk_count = minor_chunks and minor_chunks.__item_count or 0 - - if minor_chunk_count > 0 then - __lua_table_move( - minor_chunk_list, 1, minor_chunk_count, - chunk_stack_size + 1, chunk_stack) - - chunk_stack_size = chunk_stack_size + minor_chunk_count - end - end - - do - local minor_chunks = __minor_chunks[__secondary_wildcard(minor)] - local minor_chunk_list = minor_chunks and minor_chunks.__item_list - local minor_chunk_count = minor_chunks and minor_chunks.__item_count or 0 - - if minor_chunk_count > 0 then - __lua_table_move( - minor_chunk_list, 1, minor_chunk_count, - chunk_stack_size + 1, chunk_stack) - - chunk_stack_size = chunk_stack_size + minor_chunk_count - end - end - while chunk_stack_size > 0 do local chunk = chunk_stack[chunk_stack_size] @@ -2014,96 +1395,6 @@ function __chunk_without_fragment(chunk, fragment) if without_fragment_edge then return without_fragment_edge end end - if chunk.__has_pair_fragments and fragment >= __PRI_WILDCARD_OPTIONS * 2 ^ 40 then - local fragment_primary, fragment_secondary, fragment_options = - __evolved_unpack(fragment) - - if fragment_options == __ANY_WILDCARD_OPTIONS then - while chunk and chunk.__has_pair_major do - chunk = chunk.__parent - end - - if not chunk or not chunk.__has_pair_fragments then - return chunk - end - - local sib_chunk = chunk.__parent - - while sib_chunk and sib_chunk.__has_pair_fragments do - sib_chunk = sib_chunk.__parent - end - - if sib_chunk then - chunk.__without_fragment_edges[fragment] = sib_chunk - sib_chunk.__with_fragment_edges[fragment] = chunk - end - - return sib_chunk - elseif fragment_options == __PRI_WILDCARD_OPTIONS then - if not chunk.__secondary_pairs[fragment_secondary] then - -- the chunk does not have such pairs - return chunk - end - - local sib_chunk = chunk.__parent - - while sib_chunk and sib_chunk.__has_pair_fragments and sib_chunk.__secondary_pairs[fragment_secondary] do - sib_chunk = sib_chunk.__parent - end - - local ini_pair_list = chunk.__pair_list - local ini_pair_count = chunk.__pair_count - - local lst_pair_index = sib_chunk and sib_chunk.__pair_count + 2 or 2 - - for ini_pair_index = lst_pair_index, ini_pair_count do - local ini_pair = ini_pair_list[ini_pair_index] - local _, ini_pair_secondary = __evolved_unpack(ini_pair) - if ini_pair_secondary ~= fragment_secondary then - sib_chunk = __chunk_with_fragment(sib_chunk, ini_pair) - end - end - - if sib_chunk then - chunk.__without_fragment_edges[fragment] = sib_chunk - sib_chunk.__with_fragment_edges[fragment] = chunk - end - - return sib_chunk - elseif fragment_options == __SEC_WILDCARD_OPTIONS then - if not chunk.__primary_pairs[fragment_primary] then - -- the chunk does not have such pairs - return chunk - end - - local sib_chunk = chunk.__parent - - while sib_chunk and sib_chunk.__has_pair_fragments and sib_chunk.__primary_pairs[fragment_primary] do - sib_chunk = sib_chunk.__parent - end - - local ini_pair_list = chunk.__pair_list - local ini_pair_count = chunk.__pair_count - - local lst_pair_index = sib_chunk and sib_chunk.__pair_count + 2 or 2 - - for ini_pair_index = lst_pair_index, ini_pair_count do - local ini_pair = ini_pair_list[ini_pair_index] - local ini_pair_primary, _ = __evolved_unpack(ini_pair) - if ini_pair_primary ~= fragment_primary then - sib_chunk = __chunk_with_fragment(sib_chunk, ini_pair) - end - end - - if sib_chunk then - chunk.__without_fragment_edges[fragment] = sib_chunk - sib_chunk.__with_fragment_edges[fragment] = chunk - end - - return sib_chunk - end - end - if fragment > chunk.__fragment or not chunk.__fragment_set[fragment] then return chunk end @@ -2183,7 +1474,7 @@ function __chunk_without_unique_fragments(chunk) for ini_fragment_index = lst_fragment_index, ini_fragment_count do local ini_fragment = ini_fragment_list[ini_fragment_index] - if not __primary_has(ini_fragment, __UNIQUE) then + if not __evolved_has(ini_fragment, __UNIQUE) then sib_chunk = __chunk_with_fragment(sib_chunk, ini_fragment) end end @@ -2251,21 +1542,6 @@ function __chunk_has_fragment(chunk, fragment) return true end - if chunk.__has_pair_fragments and fragment >= __PRI_WILDCARD_OPTIONS * 2 ^ 40 then - local fragment_primary, fragment_secondary, fragment_options = - __evolved_unpack(fragment) - - if fragment_options == __ANY_WILDCARD_OPTIONS then - return true - elseif fragment_options == __PRI_WILDCARD_OPTIONS then - local secondary_fragments = chunk.__secondary_pairs[fragment_secondary] - return secondary_fragments and secondary_fragments.__item_count > 0 - elseif fragment_options == __SEC_WILDCARD_OPTIONS then - local primary_fragments = chunk.__primary_pairs[fragment_primary] - return primary_fragments and primary_fragments.__item_count > 0 - end - end - return false end @@ -2282,41 +1558,30 @@ function __chunk_has_all_fragments(chunk, ...) local fs = chunk.__fragment_set - local has_f = __chunk_has_fragment - local has_fs = __chunk_has_all_fragments - - local has_p = chunk.__has_pair_fragments - if fragment_count == 1 then local f1 = ... - return (has_p and has_f(chunk, f1)) - or (not has_p and fs[f1] ~= nil) + return fs[f1] ~= nil end if fragment_count == 2 then local f1, f2 = ... - return (has_p and has_f(chunk, f1) and has_f(chunk, f2)) - or (not has_p and fs[f1] ~= nil and fs[f2] ~= nil) + return fs[f1] ~= nil and fs[f2] ~= nil end if fragment_count == 3 then local f1, f2, f3 = ... - return (has_p and has_f(chunk, f1) and has_f(chunk, f2) and has_f(chunk, f3)) - or (not has_p and fs[f1] ~= nil and fs[f2] ~= nil and fs[f3] ~= nil) + return fs[f1] ~= nil and fs[f2] ~= nil and fs[f3] ~= nil end if fragment_count == 4 then local f1, f2, f3, f4 = ... - return (has_p and has_f(chunk, f1) and has_f(chunk, f2) and has_f(chunk, f3) and has_f(chunk, f4)) - or (not has_p and fs[f1] ~= nil and fs[f2] ~= nil and fs[f3] ~= nil and fs[f4] ~= nil) + return fs[f1] ~= nil and fs[f2] ~= nil and fs[f3] ~= nil and fs[f4] ~= nil end do local f1, f2, f3, f4 = ... - return (has_p and has_f(chunk, f1) and has_f(chunk, f2) and has_f(chunk, f3) and has_f(chunk, f4) - and has_fs(chunk, __lua_select(5, ...))) - or (not has_p and fs[f1] ~= nil and fs[f2] ~= nil and fs[f3] ~= nil and fs[f4] ~= nil - and has_fs(chunk, __lua_select(5, ...))) + return fs[f1] ~= nil and fs[f2] ~= nil and fs[f3] ~= nil and fs[f4] ~= nil + and __chunk_has_all_fragments(chunk, __lua_select(5, ...)) end end @@ -2332,22 +1597,10 @@ function __chunk_has_all_fragment_list(chunk, fragment_list, fragment_count) local fs = chunk.__fragment_set - local has_f = __chunk_has_fragment - local has_p = chunk.__has_pair_fragments - - if has_p then - for i = 1, fragment_count do - local f = fragment_list[i] - if not has_f(chunk, f) then - return false - end - end - else - for i = 1, fragment_count do - local f = fragment_list[i] - if fs[f] == nil then - return false - end + for i = 1, fragment_count do + local f = fragment_list[i] + if fs[f] == nil then + return false end end @@ -2367,41 +1620,30 @@ function __chunk_has_any_fragments(chunk, ...) local fs = chunk.__fragment_set - local has_f = __chunk_has_fragment - local has_fs = __chunk_has_any_fragments - - local has_p = chunk.__has_pair_fragments - if fragment_count == 1 then local f1 = ... - return (has_p and has_f(chunk, f1)) - or (not has_p and fs[f1] ~= nil) + return fs[f1] ~= nil end if fragment_count == 2 then local f1, f2 = ... - return (has_p and (has_f(chunk, f1) or has_f(chunk, f2))) - or (not has_p and (fs[f1] ~= nil or fs[f2] ~= nil)) + return fs[f1] ~= nil or fs[f2] ~= nil end if fragment_count == 3 then local f1, f2, f3 = ... - return (has_p and (has_f(chunk, f1) or has_f(chunk, f2) or has_f(chunk, f3))) - or (not has_p and (fs[f1] ~= nil or fs[f2] ~= nil or fs[f3] ~= nil)) + return fs[f1] ~= nil or fs[f2] ~= nil or fs[f3] ~= nil end if fragment_count == 4 then local f1, f2, f3, f4 = ... - return (has_p and (has_f(chunk, f1) or has_f(chunk, f2) or has_f(chunk, f3) or has_f(chunk, f4))) - or (not has_p and (fs[f1] ~= nil or fs[f2] ~= nil or fs[f3] ~= nil or fs[f4] ~= nil)) + return fs[f1] ~= nil or fs[f2] ~= nil or fs[f3] ~= nil or fs[f4] ~= nil end do local f1, f2, f3, f4 = ... - return (has_p and (has_f(chunk, f1) or has_f(chunk, f2) or has_f(chunk, f3) or has_f(chunk, f4) - or has_fs(chunk, __lua_select(5, ...)))) - or (not has_p and (fs[f1] ~= nil or fs[f2] ~= nil or fs[f3] ~= nil or fs[f4] ~= nil - or has_fs(chunk, __lua_select(5, ...)))) + return fs[f1] ~= nil or fs[f2] ~= nil or fs[f3] ~= nil or fs[f4] ~= nil + or __chunk_has_any_fragments(chunk, __lua_select(5, ...)) end end @@ -2417,22 +1659,10 @@ function __chunk_has_any_fragment_list(chunk, fragment_list, fragment_count) local fs = chunk.__fragment_set - local has_f = __chunk_has_fragment - local has_p = chunk.__has_pair_fragments - - if has_p then - for i = 1, fragment_count do - local f = fragment_list[i] - if has_f(chunk, f) then - return true - end - end - else - for i = 1, fragment_count do - local f = fragment_list[i] - if fs[f] ~= nil then - return true - end + for i = 1, fragment_count do + local f = fragment_list[i] + if fs[f] ~= nil then + return true end end @@ -2534,10 +1764,6 @@ function __chunk_required_fragments(chunk, req_fragment_set, req_fragment_list, fragment_stack[fragment_stack_size] = nil fragment_stack_size = fragment_stack_size - 1 - if stack_fragment >= __PAIR_OPTIONS * 2 ^ 40 then - stack_fragment = __evolved_unpair(stack_fragment) - end - local fragment_requires = __sorted_requires[stack_fragment] local fragment_require_list = fragment_requires and fragment_requires.__item_list local fragment_require_count = fragment_requires and fragment_requires.__item_count or 0 @@ -2584,10 +1810,6 @@ function __fragment_required_fragments(fragment, req_fragment_set, req_fragment_ fragment_stack[fragment_stack_size] = nil fragment_stack_size = fragment_stack_size - 1 - if stack_fragment >= __PAIR_OPTIONS * 2 ^ 40 then - stack_fragment = __evolved_unpair(stack_fragment) - end - local fragment_requires = __sorted_requires[stack_fragment] local fragment_require_list = fragment_requires and fragment_requires.__item_list local fragment_require_count = fragment_requires and fragment_requires.__item_count or 0 @@ -2753,7 +1975,7 @@ local function __spawn_entity(entity, components) if component_index then ---@type evolved.duplicate? local fragment_duplicate = - __primary_get(fragment, __DUPLICATE) + __evolved_get(fragment, __DUPLICATE) local new_component = component @@ -2782,7 +2004,7 @@ local function __spawn_entity(entity, components) if req_component_index then ---@type evolved.default?, evolved.duplicate? local req_fragment_default, req_fragment_duplicate = - __primary_get(req_fragment, __DEFAULT, __DUPLICATE) + __evolved_get(req_fragment, __DEFAULT, __DUPLICATE) local req_component = req_fragment_default @@ -2841,7 +2063,7 @@ local function __spawn_entity(entity, components) ---@type evolved.set_hook?, evolved.insert_hook? local fragment_on_set, fragment_on_insert = - __primary_get(fragment, __ON_SET, __ON_INSERT) + __evolved_get(fragment, __ON_SET, __ON_INSERT) local component_index = chunk_component_indices[fragment] @@ -2954,7 +2176,7 @@ local function __clone_entity(entity, prefab, components) if component_index then ---@type evolved.duplicate? local fragment_duplicate = - __primary_get(fragment, __DUPLICATE) + __evolved_get(fragment, __DUPLICATE) local prefab_component_storage = prefab_component_storages[prefab_component_index] local prefab_component = prefab_component_storage[prefab_place] @@ -3004,7 +2226,7 @@ local function __clone_entity(entity, prefab, components) if component_index then ---@type evolved.duplicate? local fragment_duplicate = - __primary_get(fragment, __DUPLICATE) + __evolved_get(fragment, __DUPLICATE) local new_component = component @@ -3033,7 +2255,7 @@ local function __clone_entity(entity, prefab, components) if req_component_index then ---@type evolved.default?, evolved.duplicate? local req_fragment_default, req_fragment_duplicate = - __primary_get(req_fragment, __DEFAULT, __DUPLICATE) + __evolved_get(req_fragment, __DEFAULT, __DUPLICATE) local req_component = req_fragment_default @@ -3092,7 +2314,7 @@ local function __clone_entity(entity, prefab, components) ---@type evolved.set_hook?, evolved.insert_hook? local fragment_on_set, fragment_on_insert = - __primary_get(fragment, __ON_SET, __ON_INSERT) + __evolved_get(fragment, __ON_SET, __ON_INSERT) local component_index = chunk_component_indices[fragment] @@ -3190,51 +2412,6 @@ local function __purge_chunk(chunk) end end - if chunk.__has_pair_fragments then - local wildcard = __ANY_WILDCARD - - local major_wildcard_chunks = __major_chunks[wildcard] - local minor_wildcard_chunks = __minor_chunks[wildcard] - - if major_wildcard_chunks and __assoc_list_remove(major_wildcard_chunks, chunk) == 0 then - __major_chunks[wildcard] = nil - end - - if minor_wildcard_chunks and __assoc_list_remove(minor_wildcard_chunks, chunk) == 0 then - __minor_chunks[wildcard] = nil - end - end - - for primary_index in __lua_next, chunk.__primary_pairs do - local secondary_wildcard = __secondary_wildcard(primary_index) - - local major_wildcard_chunks = __major_chunks[secondary_wildcard] - local minor_wildcard_chunks = __minor_chunks[secondary_wildcard] - - if major_wildcard_chunks and __assoc_list_remove(major_wildcard_chunks, chunk) == 0 then - __major_chunks[secondary_wildcard] = nil - end - - if minor_wildcard_chunks and __assoc_list_remove(minor_wildcard_chunks, chunk) == 0 then - __minor_chunks[secondary_wildcard] = nil - end - end - - for secondary_index in __lua_next, chunk.__secondary_pairs do - local primary_wildcard = __primary_wildcard(secondary_index) - - local major_wildcard_chunks = __major_chunks[primary_wildcard] - local minor_wildcard_chunks = __minor_chunks[primary_wildcard] - - if major_wildcard_chunks and __assoc_list_remove(major_wildcard_chunks, chunk) == 0 then - __major_chunks[primary_wildcard] = nil - end - - if minor_wildcard_chunks and __assoc_list_remove(minor_wildcard_chunks, chunk) == 0 then - __minor_chunks[primary_wildcard] = nil - 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, @@ -3302,7 +2479,7 @@ local function __destroy_entity_list(entity_list, entity_count) local fragment = chunk_fragment_list[chunk_fragment_index] ---@type evolved.remove_hook? - local fragment_on_remove = __primary_get(fragment, __ON_REMOVE) + local fragment_on_remove = __evolved_get(fragment, __ON_REMOVE) if fragment_on_remove then local component_index = chunk_component_indices[fragment] @@ -3378,7 +2555,7 @@ local function __destroy_fragment_list(fragment_list, fragment_count) releasing_fragment_count = releasing_fragment_count + 1 releasing_fragment_list[releasing_fragment_count] = processing_fragment - local processing_fragment_destruction_policy = __primary_get(processing_fragment, __DESTRUCTION_POLICY) + local processing_fragment_destruction_policy = __evolved_get(processing_fragment, __DESTRUCTION_POLICY) or __DESTRUCTION_POLICY_REMOVE_FRAGMENT if processing_fragment_destruction_policy == __DESTRUCTION_POLICY_DESTROY_ENTITY then @@ -3424,10 +2601,7 @@ local function __destroy_fragment_list(fragment_list, fragment_count) for i = 1, remove_fragment_policy_fragment_count do local fragment = remove_fragment_policy_fragment_list[i] - __trace_minor_chunks(fragment, __chunk_remove, - fragment, - __primary_wildcard(fragment), - __secondary_wildcard(fragment)) + __trace_minor_chunks(fragment, __chunk_remove, fragment) end __release_table(__table_pool_tag.fragment_list, remove_fragment_policy_fragment_list) @@ -3484,7 +2658,7 @@ function __chunk_set(old_chunk, fragment, component) if old_chunk_has_setup_hooks or old_chunk_has_assign_hooks then fragment_default, fragment_duplicate, fragment_on_set, fragment_on_assign = - __primary_get(fragment, __DEFAULT, __DUPLICATE, __ON_SET, __ON_ASSIGN) + __evolved_get(fragment, __DEFAULT, __DUPLICATE, __ON_SET, __ON_ASSIGN) end if fragment_on_set or fragment_on_assign then @@ -3610,7 +2784,7 @@ function __chunk_set(old_chunk, fragment, component) if new_chunk_has_setup_hooks or new_chunk_has_insert_hooks then fragment_default, fragment_duplicate, fragment_on_set, fragment_on_insert = - __primary_get(fragment, __DEFAULT, __DUPLICATE, __ON_SET, __ON_INSERT) + __evolved_get(fragment, __DEFAULT, __DUPLICATE, __ON_SET, __ON_INSERT) end if new_entity_count == 0 then @@ -3753,7 +2927,7 @@ function __chunk_set(old_chunk, fragment, component) if new_chunk_has_setup_hooks or new_chunk_has_insert_hooks then req_fragment_default, req_fragment_duplicate, req_fragment_on_set, req_fragment_on_insert = - __primary_get(req_fragment, __DEFAULT, __DUPLICATE, __ON_SET, __ON_INSERT) + __evolved_get(req_fragment, __DEFAULT, __DUPLICATE, __ON_SET, __ON_INSERT) end if req_fragment_on_set or req_fragment_on_insert then @@ -3890,7 +3064,7 @@ function __chunk_remove(old_chunk, ...) if not new_fragment_set[fragment] then ---@type evolved.remove_hook? - local fragment_on_remove = __primary_get(fragment, __ON_REMOVE) + local fragment_on_remove = __evolved_get(fragment, __ON_REMOVE) if fragment_on_remove then local old_component_index = old_component_indices[fragment] @@ -4006,7 +3180,7 @@ function __chunk_clear(chunk) local fragment = chunk_fragment_list[chunk_fragment_index] ---@type evolved.remove_hook? - local fragment_on_remove = __primary_get(fragment, __ON_REMOVE) + local fragment_on_remove = __evolved_get(fragment, __ON_REMOVE) if fragment_on_remove then local component_index = chunk_component_indices[fragment] @@ -4859,12 +4033,10 @@ end ---@param id evolved.id ---@return integer primary ---@return integer secondary ----@return integer options ---@nodiscard function __evolved_unpack(id) return id % 2 ^ 20, - (id - id % 2 ^ 20) / 2 ^ 20 % 2 ^ 20, - (id - id % 2 ^ 40) / 2 ^ 40 % 2 ^ 12 + (id - id % 2 ^ 20) / 2 ^ 20 % 2 ^ 20 end ---@return boolean started @@ -4977,22 +4149,10 @@ end ---@return boolean ---@nodiscard function __evolved_alive(entity) - local entity_primary, entity_secondary, entity_options = __evolved_unpack(entity) + local entity_primary, _ = __evolved_unpack(entity) - if entity_options < __PAIR_OPTIONS then - if __freelist_ids[entity_primary] ~= entity then - return false - end - else - local entity_primary_id = __freelist_ids[entity_primary] --[[@as evolved.id?]] - if not entity_primary_id or entity_primary_id % 2 ^ 20 ~= entity_primary then - return false - end - - local entity_secondary_id = __freelist_ids[entity_secondary] --[[@as evolved.id?]] - if not entity_secondary_id or entity_secondary_id % 2 ^ 20 ~= entity_secondary then - return false - end + if __freelist_ids[entity_primary] ~= entity then + return false end return true @@ -5044,11 +4204,7 @@ end ---@return boolean ---@nodiscard function __evolved_empty(entity) - local entity_primary, _, entity_options = __evolved_unpack(entity) - - if entity_options >= __PAIR_OPTIONS then - return true - end + local entity_primary, _ = __evolved_unpack(entity) if __freelist_ids[entity_primary] ~= entity then return true @@ -5104,11 +4260,7 @@ end ---@return boolean ---@nodiscard function __evolved_has(entity, fragment) - local entity_primary, _, entity_options = __evolved_unpack(entity) - - if entity_options >= __PAIR_OPTIONS then - return false - end + local entity_primary, _ = __evolved_unpack(entity) if __freelist_ids[entity_primary] ~= entity then return false @@ -5130,11 +4282,7 @@ function __evolved_has_all(entity, ...) return true end - local entity_primary, _, entity_options = __evolved_unpack(entity) - - if entity_options >= __PAIR_OPTIONS then - return false - end + local entity_primary, _ = __evolved_unpack(entity) if __freelist_ids[entity_primary] ~= entity then return false @@ -5156,11 +4304,7 @@ function __evolved_has_any(entity, ...) return false end - local entity_primary, _, entity_options = __evolved_unpack(entity) - - if entity_options >= __PAIR_OPTIONS then - return false - end + local entity_primary, _ = __evolved_unpack(entity) if __freelist_ids[entity_primary] ~= entity then return false @@ -5176,11 +4320,7 @@ end ---@return evolved.component ... components ---@nodiscard function __evolved_get(entity, ...) - local entity_primary, _, entity_options = __evolved_unpack(entity) - - if entity_options >= __PAIR_OPTIONS then - return - end + local entity_primary, _ = __evolved_unpack(entity) if __freelist_ids[entity_primary] ~= entity then return @@ -5200,45 +4340,19 @@ end ---@param fragment evolved.fragment ---@param component evolved.component function __evolved_set(entity, fragment, component) - local entity_primary, _, entity_options = __evolved_unpack(entity) + local entity_primary, _ = __evolved_unpack(entity) - if entity_options >= __PAIR_OPTIONS then - __error_fmt('the pair (%s) cannot be changed', - __universal_name(entity)) - elseif __freelist_ids[entity_primary] ~= entity then + if __freelist_ids[entity_primary] ~= entity then __error_fmt('the id (%s) is not alive and cannot be changed', __universal_name(entity)) end - local fragment_primary, fragment_secondary, fragment_options = - __evolved_unpack(fragment) + local fragment_primary, _ = __evolved_unpack(fragment) if __debug_mode then - if fragment_options < __PAIR_OPTIONS then - if fragment_primary == __ANY_INDEX then - __error_fmt('the id (%s) is a wildcard and cannot be set', - __universal_name(fragment)) - elseif __freelist_ids[fragment_primary] ~= fragment then - __error_fmt('the id (%s) is not alive and cannot be set', - __universal_name(fragment)) - end - else - if fragment_options >= __PRI_WILDCARD_OPTIONS then - __error_fmt('the pair (%s) is a wildcard and cannot be set', - __universal_name(fragment)) - end - - local fragment_primary_id = __freelist_ids[fragment_primary] --[[@as evolved.id?]] - if not fragment_primary_id or fragment_primary_id % 2 ^ 20 ~= fragment_primary then - __error_fmt('the pair (%s) has no alive primary id and cannot be set', - __universal_name(fragment)) - end - - local fragment_secondary_id = __freelist_ids[fragment_secondary] --[[@as evolved.id?]] - if not fragment_secondary_id or fragment_secondary_id % 2 ^ 20 ~= fragment_secondary then - __error_fmt('the pair (%s) has no alive secondary id and cannot be set', - __universal_name(fragment)) - end + if __freelist_ids[fragment_primary] ~= fragment then + __error_fmt('the id (%s) is not alive and cannot be set', + __universal_name(fragment)) end end @@ -5273,7 +4387,7 @@ function __evolved_set(entity, fragment, component) if old_chunk_has_setup_hooks or old_chunk_has_assign_hooks then fragment_default, fragment_duplicate, fragment_on_set, fragment_on_assign = - __primary_get(fragment, __DEFAULT, __DUPLICATE, __ON_SET, __ON_ASSIGN) + __evolved_get(fragment, __DEFAULT, __DUPLICATE, __ON_SET, __ON_ASSIGN) end local old_component_index = old_component_indices[fragment] @@ -5343,7 +4457,7 @@ function __evolved_set(entity, fragment, component) if new_chunk_has_setup_hooks or new_chunk_has_insert_hooks then fragment_default, fragment_duplicate, fragment_on_set, fragment_on_insert = - __primary_get(fragment, __DEFAULT, __DUPLICATE, __ON_SET, __ON_INSERT) + __evolved_get(fragment, __DEFAULT, __DUPLICATE, __ON_SET, __ON_INSERT) end local new_place = new_entity_count + 1 @@ -5416,7 +4530,7 @@ function __evolved_set(entity, fragment, component) if new_chunk_has_setup_hooks or new_chunk_has_insert_hooks then req_fragment_default, req_fragment_duplicate, req_fragment_on_set, req_fragment_on_insert = - __primary_get(req_fragment, __DEFAULT, __DUPLICATE, __ON_SET, __ON_INSERT) + __evolved_get(req_fragment, __DEFAULT, __DUPLICATE, __ON_SET, __ON_INSERT) end local req_component_index = new_component_indices[req_fragment] @@ -5476,12 +4590,9 @@ function __evolved_remove(entity, ...) return end - local entity_primary, _, entity_options = __evolved_unpack(entity) + local entity_primary, _ = __evolved_unpack(entity) - if entity_options >= __PAIR_OPTIONS then - __error_fmt('the pair (%s) cannot be changed', - __universal_name(entity)) - elseif __freelist_ids[entity_primary] ~= entity then + if __freelist_ids[entity_primary] ~= entity then -- the id is not alive, nothing to remove return end @@ -5520,7 +4631,7 @@ function __evolved_remove(entity, ...) if not new_fragment_set[fragment] then ---@type evolved.remove_hook? - local fragment_on_remove = __primary_get(fragment, __ON_REMOVE) + local fragment_on_remove = __evolved_get(fragment, __ON_REMOVE) if fragment_on_remove then local old_component_index = old_component_indices[fragment] @@ -5594,12 +4705,9 @@ function __evolved_clear(...) for argument_index = 1, argument_count do ---@type evolved.entity local entity = __lua_select(argument_index, ...) - local entity_primary, _, entity_options = __evolved_unpack(entity) + local entity_primary, _ = __evolved_unpack(entity) - if entity_options >= __PAIR_OPTIONS then - __warning_fmt('the pair (%s) cannot be changed', - __universal_name(entity)) - elseif __freelist_ids[entity_primary] ~= entity then + if __freelist_ids[entity_primary] ~= entity then -- the id is not alive, nothing to clear else local chunk = entity_chunks[entity_primary] @@ -5615,7 +4723,7 @@ function __evolved_clear(...) local fragment = chunk_fragment_list[chunk_fragment_index] ---@type evolved.remove_hook? - local fragment_on_remove = __primary_get(fragment, __ON_REMOVE) + local fragment_on_remove = __evolved_get(fragment, __ON_REMOVE) if fragment_on_remove then local component_index = chunk_component_indices[fragment] @@ -5673,18 +4781,12 @@ function __evolved_destroy(...) for argument_index = 1, argument_count do ---@type evolved.entity local entity = __lua_select(argument_index, ...) - local entity_primary, _, entity_options = __evolved_unpack(entity) + local entity_primary, _ = __evolved_unpack(entity) - if entity_options >= __PAIR_OPTIONS then - __warning_fmt('the pair (%s) cannot be changed', - __universal_name(entity)) - elseif __freelist_ids[entity_primary] ~= entity then + if __freelist_ids[entity_primary] ~= entity then -- the id is not alive, nothing to destroy else - local is_fragment = - minor_chunks[entity] or - minor_chunks[__primary_wildcard(entity)] or - minor_chunks[__secondary_wildcard(entity)] + local is_fragment = minor_chunks[entity] if not is_fragment then purging_entity_count = purging_entity_count + 1 @@ -5718,45 +4820,19 @@ end ---@param fragment evolved.fragment ---@param component evolved.component function __evolved_batch_set(query, fragment, component) - local query_primary, _, query_options = __evolved_unpack(query) + local query_primary, _ = __evolved_unpack(query) - if query_options >= __PAIR_OPTIONS then - __error_fmt('the pair (%s) cannot be queried', - __universal_name(query)) - elseif __freelist_ids[query_primary] ~= query then + if __freelist_ids[query_primary] ~= query then __error_fmt('the id (%s) is not alive and cannot be queried', __universal_name(query)) end if __debug_mode then - local fragment_primary, fragment_secondary, fragment_options = - __evolved_unpack(fragment) + local fragment_primary, _ = __evolved_unpack(fragment) - if fragment_options < __PAIR_OPTIONS then - if fragment_primary == __ANY_INDEX then - __error_fmt('the id (%s) is a wildcard and cannot be set', - __universal_name(fragment)) - elseif __freelist_ids[fragment_primary] ~= fragment then - __error_fmt('the id (%s) is not alive and cannot be set', - __universal_name(fragment)) - end - else - if fragment_options >= __PRI_WILDCARD_OPTIONS then - __error_fmt('the pair (%s) is a wildcard and cannot be set', - __universal_name(fragment)) - end - - local fragment_primary_id = __freelist_ids[fragment_primary] --[[@as evolved.id?]] - if not fragment_primary_id or fragment_primary_id % 2 ^ 20 ~= fragment_primary then - __error_fmt('the pair (%s) has no alive primary id and cannot be set', - __universal_name(fragment)) - end - - local fragment_secondary_id = __freelist_ids[fragment_secondary] --[[@as evolved.id?]] - if not fragment_secondary_id or fragment_secondary_id % 2 ^ 20 ~= fragment_secondary then - __error_fmt('the pair (%s) has no alive secondary id and cannot be set', - __universal_name(fragment)) - end + if __freelist_ids[fragment_primary] ~= fragment then + __error_fmt('the id (%s) is not alive and cannot be set', + __universal_name(fragment)) end end @@ -5797,12 +4873,9 @@ function __evolved_batch_remove(query, ...) return end - local query_primary, _, query_options = __evolved_unpack(query) + local query_primary, _ = __evolved_unpack(query) - if query_options >= __PAIR_OPTIONS then - __error_fmt('the pair (%s) cannot be queried', - __universal_name(query)) - elseif __freelist_ids[query_primary] ~= query then + if __freelist_ids[query_primary] ~= query then __error_fmt('the id (%s) is not alive and cannot be queried', __universal_name(query)) end @@ -5858,12 +4931,9 @@ function __evolved_batch_clear(...) for argument_index = 1, argument_count do ---@type evolved.query local query = __lua_select(argument_index, ...) - local query_primary, _, query_options = __evolved_unpack(query) + local query_primary, _ = __evolved_unpack(query) - if query_options >= __PAIR_OPTIONS then - __warning_fmt('the pair (%s) cannot be queried', - __universal_name(query)) - elseif __freelist_ids[query_primary] ~= query then + if __freelist_ids[query_primary] ~= query then __warning_fmt('the id (%s) is not alive and cannot be queried', __universal_name(query)) else @@ -5915,12 +4985,9 @@ function __evolved_batch_destroy(...) for argument_index = 1, argument_count do ---@type evolved.query local query = __lua_select(argument_index, ...) - local query_primary, _, query_options = __evolved_unpack(query) + local query_primary, _ = __evolved_unpack(query) - if query_options >= __PAIR_OPTIONS then - __warning_fmt('the pair (%s) cannot be queried', - __universal_name(query)) - elseif __freelist_ids[query_primary] ~= query then + if __freelist_ids[query_primary] ~= query then __warning_fmt('the id (%s) is not alive and cannot be queried', __universal_name(query)) else @@ -5931,10 +4998,7 @@ function __evolved_batch_destroy(...) for i = 1, entity_count do local entity = entity_list[i] - local is_fragment = - minor_chunks[entity] or - minor_chunks[__primary_wildcard(entity)] or - minor_chunks[__secondary_wildcard(entity)] + local is_fragment = minor_chunks[entity] if not is_fragment then purging_entity_count = purging_entity_count + 1 @@ -5978,12 +5042,9 @@ end ---@return evolved.each_state? iterator_state ---@nodiscard function __evolved_each(entity) - local entity_primary, _, entity_options = __evolved_unpack(entity) + local entity_primary, _ = __evolved_unpack(entity) - if entity_options >= __PAIR_OPTIONS then - __error_fmt('the pair (%s) cannot be iterated', - __universal_name(entity)) - elseif __freelist_ids[entity_primary] ~= entity then + if __freelist_ids[entity_primary] ~= entity then __error_fmt('the id (%s) is not alive and cannot be iterated', __universal_name(entity)) end @@ -6010,12 +5071,9 @@ end ---@return evolved.execute_state? iterator_state ---@nodiscard function __evolved_execute(query) - local query_primary, _, query_options = __evolved_unpack(query) + local query_primary, _ = __evolved_unpack(query) - if query_options >= __PAIR_OPTIONS then - __error_fmt('the pair (%s) cannot be executed', - __universal_name(query)) - elseif __freelist_ids[query_primary] ~= query then + if __freelist_ids[query_primary] ~= query then __error_fmt('the id (%s) is not alive and cannot be executed', __universal_name(query)) end @@ -6037,134 +5095,56 @@ function __evolved_execute(query) if query_include_count > 0 then local query_major = query_include_list[query_include_count] - if query_major >= __PRI_WILDCARD_OPTIONS * 2 ^ 40 then - local minor_chunks = __minor_chunks[query_major] - local minor_chunk_list = minor_chunks and minor_chunks.__item_list - local minor_chunk_count = minor_chunks and minor_chunks.__item_count or 0 + local major_chunks = __major_chunks[query_major] + local major_chunk_list = major_chunks and major_chunks.__item_list + local major_chunk_count = major_chunks and major_chunks.__item_count or 0 - for query_include_index = 1, query_include_count - 1 do - local query_minor = query_include_list[query_include_index] + for major_chunk_index = 1, major_chunk_count do + local major_chunk = major_chunk_list[major_chunk_index] - local query_chunks = __minor_chunks[query_minor] - local query_chunk_list = query_chunks and query_chunks.__item_list - local query_chunk_count = query_chunks and query_chunks.__item_count or 0 + local is_major_chunk_matched = true - if query_chunk_count < minor_chunk_count then - minor_chunks, minor_chunk_list, minor_chunk_count = - query_chunks, query_chunk_list, query_chunk_count + if is_major_chunk_matched and query_include_count > 1 then + is_major_chunk_matched = __chunk_has_all_fragment_list( + major_chunk, query_include_list, query_include_count - 1) + end - if query_chunk_count == 0 then + if is_major_chunk_matched and query_exclude_count > 0 then + is_major_chunk_matched = not __chunk_has_any_fragment_list( + major_chunk, query_exclude_list, query_exclude_count) + end + + if is_major_chunk_matched and major_chunk.__has_explicit_minors then + local major_chunk_minor_list = major_chunk.__fragment_list + local major_chunk_minor_count = major_chunk.__fragment_count - 1 + + for major_chunk_fragment_index = 1, major_chunk_minor_count do + local major_chunk_minor = major_chunk_minor_list[major_chunk_fragment_index] + + local is_major_chunk_minor_included = query_include_set[major_chunk_minor] + + if not is_major_chunk_minor_included and __evolved_has(major_chunk_minor, __EXPLICIT) then + is_major_chunk_matched = false break end end end - for minor_chunk_index = 1, minor_chunk_count do - local minor_chunk = minor_chunk_list[minor_chunk_index] - - local is_minor_chunk_matched = true - - if is_minor_chunk_matched and minor_chunk.__entity_count == 0 then - is_minor_chunk_matched = false - end - - if is_minor_chunk_matched then - is_minor_chunk_matched = __chunk_has_all_fragment_list( - minor_chunk, query_include_list, query_include_count) - end - - if is_minor_chunk_matched and query_exclude_count > 0 then - is_minor_chunk_matched = not __chunk_has_any_fragment_list( - minor_chunk, query_exclude_list, query_exclude_count) - end - - if is_minor_chunk_matched and minor_chunk.__has_explicit_fragments then - local minor_chunk_fragment_list = minor_chunk.__fragment_list - local minor_chunk_fragment_count = minor_chunk.__fragment_count - - for minor_chunk_fragment_index = 1, minor_chunk_fragment_count do - local minor_chunk_fragment = minor_chunk_fragment_list[minor_chunk_fragment_index] - - local is_minor_chunk_fragment_included = - query_include_set[minor_chunk_fragment] or - query_include_set[__secondary_wildcard(__evolved_unpack(minor_chunk_fragment))] - - if not is_minor_chunk_fragment_included and __primary_has(minor_chunk_fragment, __EXPLICIT) then - is_minor_chunk_matched = false - break - end - end - end - - if is_minor_chunk_matched then - chunk_stack_size = chunk_stack_size + 1 - chunk_stack[chunk_stack_size] = minor_chunk - end + if is_major_chunk_matched then + chunk_stack_size = chunk_stack_size + 1 + chunk_stack[chunk_stack_size] = major_chunk end - - ---@type evolved.execute_state - local execute_state = __acquire_table(__table_pool_tag.execute_state) - - execute_state[1] = __structural_changes - execute_state[2] = chunk_stack - execute_state[3] = chunk_stack_size - execute_state[4] = query_exclude_set - - return __iterator_fns.__execute_minor_iterator, execute_state - else - local major_chunks = __major_chunks[query_major] - local major_chunk_list = major_chunks and major_chunks.__item_list - local major_chunk_count = major_chunks and major_chunks.__item_count or 0 - - for major_chunk_index = 1, major_chunk_count do - local major_chunk = major_chunk_list[major_chunk_index] - - local is_major_chunk_matched = true - - if is_major_chunk_matched and query_include_count > 1 then - is_major_chunk_matched = __chunk_has_all_fragment_list( - major_chunk, query_include_list, query_include_count - 1) - end - - if is_major_chunk_matched and query_exclude_count > 0 then - is_major_chunk_matched = not __chunk_has_any_fragment_list( - major_chunk, query_exclude_list, query_exclude_count) - end - - if is_major_chunk_matched and major_chunk.__has_explicit_minors then - local major_chunk_minor_list = major_chunk.__fragment_list - local major_chunk_minor_count = major_chunk.__fragment_count - 1 - - for major_chunk_fragment_index = 1, major_chunk_minor_count do - local major_chunk_minor = major_chunk_minor_list[major_chunk_fragment_index] - - local is_major_chunk_minor_included = - query_include_set[major_chunk_minor] or - query_include_set[__secondary_wildcard(__evolved_unpack(major_chunk_minor))] - - if not is_major_chunk_minor_included and __primary_has(major_chunk_minor, __EXPLICIT) then - is_major_chunk_matched = false - break - end - end - end - - if is_major_chunk_matched then - chunk_stack_size = chunk_stack_size + 1 - chunk_stack[chunk_stack_size] = major_chunk - end - end - - ---@type evolved.execute_state - local execute_state = __acquire_table(__table_pool_tag.execute_state) - - execute_state[1] = __structural_changes - execute_state[2] = chunk_stack - execute_state[3] = chunk_stack_size - execute_state[4] = query_exclude_set - - return __iterator_fns.__execute_major_iterator, execute_state end + + ---@type evolved.execute_state + local execute_state = __acquire_table(__table_pool_tag.execute_state) + + execute_state[1] = __structural_changes + execute_state[2] = chunk_stack + execute_state[3] = chunk_stack_size + execute_state[4] = query_exclude_set + + return __iterator_fns.__execute_iterator, execute_state else for _, root_chunk in __lua_next, __root_chunks do local is_root_chunk_matched = true @@ -6192,7 +5172,7 @@ function __evolved_execute(query) execute_state[3] = chunk_stack_size execute_state[4] = query_exclude_set - return __iterator_fns.__execute_major_iterator, execute_state + return __iterator_fns.__execute_iterator, execute_state end end @@ -6207,12 +5187,9 @@ function __evolved_process(...) for argument_index = 1, argument_count do ---@type evolved.system local system = __lua_select(argument_index, ...) - local system_primary, _, system_options = __evolved_unpack(system) + local system_primary, _ = __evolved_unpack(system) - if system_options >= __PAIR_OPTIONS then - __warning_fmt('the pair (%s) cannot be processed', - __universal_name(system)) - elseif __freelist_ids[system_primary] ~= system then + if __freelist_ids[system_primary] ~= system then __warning_fmt('the id (%s) is not alive and cannot be processed', __universal_name(system)) elseif __evolved_has(system, __DISABLED) then @@ -6294,340 +5271,6 @@ function __evolved_collect_garbage() __evolved_commit() end ----@param primary evolved.id ----@param secondary evolved.id ----@return evolved.pair pair ----@nodiscard -function __evolved_pair(primary, secondary) - local primary_index, _, primary_options = __evolved_unpack(primary) - if primary_options >= __PAIR_OPTIONS then - __error_fmt('the primary id (%s) is a pair and cannot be used as a primary id of a new pair', - __universal_name(primary)) - end - - local secondary_index, _, secondary_options = __evolved_unpack(secondary) - if secondary_options >= __PAIR_OPTIONS then - __error_fmt('the secondary id (%s) is a pair and cannot be used as a secondary id of a new pair', - __universal_name(secondary)) - end - - local pair_options = __PAIR_OPTIONS - - if primary_index == __ANY_INDEX and secondary_index == __ANY_INDEX then - pair_options = __ANY_WILDCARD_OPTIONS - elseif primary_index == __ANY_INDEX then - pair_options = __PRI_WILDCARD_OPTIONS - elseif secondary_index == __ANY_INDEX then - pair_options = __SEC_WILDCARD_OPTIONS - end - - return primary_index + secondary_index * 2 ^ 20 + pair_options * 2 ^ 40 --[[@as evolved.pair]] -end - ----@param pair evolved.pair ----@return evolved.id primary ----@return evolved.id secondary ----@nodiscard -function __evolved_unpair(pair) - local pair_primary, pair_secondary, pair_options = __evolved_unpack(pair) - if pair_options < __PAIR_OPTIONS then - __error_fmt('the id (%s) is not a pair and cannot be unpaired', - __universal_name(pair)) - end - - local pair_primary_id = __freelist_ids[pair_primary] --[[@as evolved.id?]] - if not pair_primary_id or pair_primary_id % 2 ^ 20 ~= pair_primary then - __error_fmt('the pair (%s) has not alive primary id and cannot be unpaired', - __universal_name(pair)) - else - ---@cast pair_primary_id -? - end - - local pair_secondary_id = __freelist_ids[pair_secondary] --[[@as evolved.id?]] - if not pair_secondary_id or pair_secondary_id % 2 ^ 20 ~= pair_secondary then - __error_fmt('the pair (%s) has not alive secondary id and cannot be unpaired', - __universal_name(pair)) - else - ---@cast pair_secondary_id -? - end - - return pair_primary_id, pair_secondary_id -end - ----@param id evolved.id ----@return boolean ----@nodiscard -function __evolved_is_pair(id) - return id >= __PAIR_OPTIONS * 2 ^ 40 -end - ----@param id evolved.id ----@return boolean ----@nodiscard -function __evolved_is_wildcard(id) - return id >= __PRI_WILDCARD_OPTIONS * 2 ^ 40 -end - ----@param entity evolved.entity ----@param secondary evolved.fragment ----@param index? integer ----@return evolved.fragment? primary ----@return evolved.component? component ----@nodiscard -function __evolved_primary(entity, secondary, index) - index = index or 1 - - local entity_primary, _, entity_options = __evolved_unpack(entity) - - if entity_options >= __PAIR_OPTIONS then - return - end - - if __freelist_ids[entity_primary] ~= entity then - return - end - - local entity_chunk = __entity_chunks[entity_primary] - - if not entity_chunk then - return - end - - local secondary_index, _, secondary_options = __evolved_unpack(secondary) - - if secondary_options >= __PAIR_OPTIONS then - __error_fmt('the pair (%s) cannot be used as a secondary fragment', - __universal_name(secondary)) - end - - local secondary_fragments = entity_chunk.__secondary_pairs[secondary_index] - local secondary_fragment_list = secondary_fragments and secondary_fragments.__item_list - local secondary_fragment_count = secondary_fragments and secondary_fragments.__item_count or 0 - - if index < 1 or index > secondary_fragment_count then - return - end - - local secondary_fragment = secondary_fragment_list[index] - local primary, _ = __evolved_unpair(secondary_fragment) - - local component_index = entity_chunk.__component_indices[secondary_fragment] - local component_storage = entity_chunk.__component_storages[component_index] - - local entity_place = __entity_places[entity_primary] - return primary, component_storage and component_storage[entity_place] -end - ----@param entity evolved.entity ----@param primary evolved.fragment ----@param index? integer ----@return evolved.fragment? secondary ----@return evolved.component? component ----@nodiscard -function __evolved_secondary(entity, primary, index) - index = index or 1 - - local entity_primary, _, entity_options = __evolved_unpack(entity) - - if entity_options >= __PAIR_OPTIONS then - return - end - - if __freelist_ids[entity_primary] ~= entity then - return - end - - local entity_chunk = __entity_chunks[entity_primary] - - if not entity_chunk then - return - end - - local primary_index, _, primary_options = __evolved_unpack(primary) - - if primary_options >= __PAIR_OPTIONS then - __error_fmt('the pair (%s) cannot be used as a primary fragment', - __universal_name(primary)) - end - - local primary_fragments = entity_chunk.__primary_pairs[primary_index] - local primary_fragment_list = primary_fragments and primary_fragments.__item_list - local primary_fragment_count = primary_fragments and primary_fragments.__item_count or 0 - - if index < 1 or index > primary_fragment_count then - return - end - - local primary_fragment = primary_fragment_list[index] - local _, secondary = __evolved_unpair(primary_fragment) - - local component_index = entity_chunk.__component_indices[primary_fragment] - local component_storage = entity_chunk.__component_storages[component_index] - - local entity_place = __entity_places[entity_primary] - return secondary, component_storage and component_storage[entity_place] -end - ----@param entity evolved.entity ----@param secondary evolved.fragment ----@return evolved.primaries_iterator iterator ----@return evolved.primaries_state? iterator_state ----@nodiscard -function __evolved_primaries(entity, secondary) - local entity_primary, _, entity_options = __evolved_unpack(entity) - - if entity_options >= __PAIR_OPTIONS then - return __iterator_fns.__primaries_iterator - end - - if __freelist_ids[entity_primary] ~= entity then - return __iterator_fns.__primaries_iterator - end - - local entity_chunk = __entity_chunks[entity_primary] - - if not entity_chunk then - return __iterator_fns.__primaries_iterator - end - - local secondary_index, _, secondary_options = __evolved_unpack(secondary) - - if secondary_options >= __PAIR_OPTIONS then - __error_fmt('the pair (%s) cannot be used as a secondary fragment', - __universal_name(secondary)) - end - - local secondary_fragments = entity_chunk.__secondary_pairs[secondary_index] - - if not secondary_fragments or secondary_fragments.__item_count == 0 then - return __iterator_fns.__primaries_iterator - end - - ---@type evolved.primaries_state - local primaries_state = __acquire_table(__table_pool_tag.primaries_state) - - primaries_state[1] = __structural_changes - primaries_state[2] = entity_chunk - primaries_state[3] = __entity_places[entity_primary] - primaries_state[4] = secondary_index - primaries_state[5] = 1 - - return __iterator_fns.__primaries_iterator, primaries_state -end - ----@param entity evolved.entity ----@param primary evolved.fragment ----@return evolved.secondaries_iterator iterator ----@return evolved.secondaries_state? iterator_state ----@nodiscard -function __evolved_secondaries(entity, primary) - local entity_primary, _, entity_options = __evolved_unpack(entity) - - if entity_options >= __PAIR_OPTIONS then - return __iterator_fns.__secondaries_iterator - end - - if __freelist_ids[entity_primary] ~= entity then - return __iterator_fns.__secondaries_iterator - end - - local entity_chunk = __entity_chunks[entity_primary] - - if not entity_chunk then - return __iterator_fns.__secondaries_iterator - end - - local primary_index, _, primary_options = __evolved_unpack(primary) - - if primary_options >= __PAIR_OPTIONS then - __error_fmt('the pair (%s) cannot be used as a primary fragment', - __universal_name(primary)) - end - - local primary_fragments = entity_chunk.__primary_pairs[primary_index] - - if not primary_fragments or primary_fragments.__item_count == 0 then - return __iterator_fns.__secondaries_iterator - end - - ---@type evolved.secondaries_state - local secondaries_state = __acquire_table(__table_pool_tag.secondaries_state) - - secondaries_state[1] = __structural_changes - secondaries_state[2] = entity_chunk - secondaries_state[3] = __entity_places[entity_primary] - secondaries_state[4] = primary_index - secondaries_state[5] = 1 - - return __iterator_fns.__secondaries_iterator, secondaries_state -end - ----@param entity evolved.entity ----@param secondary evolved.fragment ----@return integer ----@nodiscard -function __evolved_primary_count(entity, secondary) - local entity_primary, _, entity_options = __evolved_unpack(entity) - - if entity_options >= __PAIR_OPTIONS then - return 0 - end - - if __freelist_ids[entity_primary] ~= entity then - return 0 - end - - local entity_chunk = __entity_chunks[entity_primary] - - if not entity_chunk then - return 0 - end - - local secondary_index, _, secondary_options = __evolved_unpack(secondary) - - if secondary_options >= __PAIR_OPTIONS then - __error_fmt('the pair (%s) cannot be used as a secondary fragment', - __universal_name(secondary)) - end - - local secondary_fragments = entity_chunk.__secondary_pairs[secondary_index] - - return secondary_fragments and secondary_fragments.__item_count or 0 -end - ----@param entity evolved.entity ----@param primary evolved.fragment ----@return integer ----@nodiscard -function __evolved_secondary_count(entity, primary) - local entity_primary, _, entity_options = __evolved_unpack(entity) - - if entity_options >= __PAIR_OPTIONS then - return 0 - end - - if __freelist_ids[entity_primary] ~= entity then - return 0 - end - - local entity_chunk = __entity_chunks[entity_primary] - - if not entity_chunk then - return 0 - end - - local primary_index, _, primary_options = __evolved_unpack(primary) - - if primary_options >= __PAIR_OPTIONS then - __error_fmt('the pair (%s) cannot be used as a primary fragment', - __universal_name(primary)) - end - - local primary_fragments = entity_chunk.__primary_pairs[primary_index] - - return primary_fragments and primary_fragments.__item_count or 0 -end - --- --- --- @@ -6816,30 +5459,6 @@ function __builder_mt:has(fragment) return true end - local primary_pairs = self.__primary_pairs - local secondary_pairs = self.__secondary_pairs - - local maybe_has_pairs = primary_pairs and secondary_pairs - - if maybe_has_pairs and fragment >= __PRI_WILDCARD_OPTIONS * 2 ^ 40 then - ---@cast primary_pairs -? - ---@cast secondary_pairs -? - - local fragment_primary, fragment_secondary, fragment_options = - __evolved_unpack(fragment) - - if fragment_options == __ANY_WILDCARD_OPTIONS then - return __lua_next(primary_pairs) ~= nil - and __lua_next(secondary_pairs) ~= nil - elseif fragment_options == __PRI_WILDCARD_OPTIONS then - local secondary_fragments = secondary_pairs[fragment_secondary] - return secondary_fragments ~= nil and secondary_fragments.__item_count > 0 - elseif fragment_options == __SEC_WILDCARD_OPTIONS then - local primary_fragments = primary_pairs[fragment_primary] - return primary_fragments ~= nil and primary_fragments.__item_count > 0 - end - end - return false end @@ -6855,41 +5474,30 @@ function __builder_mt:has_all(...) local cs = self.__components - local has_f = self.has - local has_fs = self.has_all - - local m_has_p = self.__primary_pairs and self.__secondary_pairs - if fragment_count == 1 then local f1 = ... - return (m_has_p and has_f(self, f1)) - or (not m_has_p and cs[f1] ~= nil) + return cs[f1] ~= nil end if fragment_count == 2 then local f1, f2 = ... - return (m_has_p and has_f(self, f1) and has_f(self, f2)) - or (not m_has_p and cs[f1] ~= nil and cs[f2] ~= nil) + return cs[f1] ~= nil and cs[f2] ~= nil end if fragment_count == 3 then local f1, f2, f3 = ... - return (m_has_p and has_f(self, f1) and has_f(self, f2) and has_f(self, f3)) - or (not m_has_p and cs[f1] ~= nil and cs[f2] ~= nil and cs[f3] ~= nil) + return cs[f1] ~= nil and cs[f2] ~= nil and cs[f3] ~= nil end if fragment_count == 4 then local f1, f2, f3, f4 = ... - return (m_has_p and has_f(self, f1) and has_f(self, f2) and has_f(self, f3) and has_f(self, f4)) - or (not m_has_p and cs[f1] ~= nil and cs[f2] ~= nil and cs[f3] ~= nil and cs[f4] ~= nil) + return cs[f1] ~= nil and cs[f2] ~= nil and cs[f3] ~= nil and cs[f4] ~= nil end do local f1, f2, f3, f4 = ... - return (m_has_p and has_f(self, f1) and has_f(self, f2) and has_f(self, f3) and has_f(self, f4) - and has_fs(self, __lua_select(5, ...))) - or (not m_has_p and cs[f1] ~= nil and cs[f2] ~= nil and cs[f3] ~= nil and cs[f4] ~= nil - and has_fs(self, __lua_select(5, ...))) + return cs[f1] ~= nil and cs[f2] ~= nil and cs[f3] ~= nil and cs[f4] ~= nil + and self:has_all(__lua_select(5, ...)) end end @@ -6905,41 +5513,30 @@ function __builder_mt:has_any(...) local cs = self.__components - local has_f = self.has - local has_fs = self.has_any - - local m_has_p = self.__primary_pairs and self.__secondary_pairs - if fragment_count == 1 then local f1 = ... - return (m_has_p and has_f(self, f1)) - or (not m_has_p and cs[f1] ~= nil) + return cs[f1] ~= nil end if fragment_count == 2 then local f1, f2 = ... - return (m_has_p and (has_f(self, f1) or has_f(self, f2))) - or (not m_has_p and (cs[f1] ~= nil or cs[f2] ~= nil)) + return cs[f1] ~= nil or cs[f2] ~= nil end if fragment_count == 3 then local f1, f2, f3 = ... - return (m_has_p and (has_f(self, f1) or has_f(self, f2) or has_f(self, f3))) - or (not m_has_p and (cs[f1] ~= nil or cs[f2] ~= nil or cs[f3] ~= nil)) + return cs[f1] ~= nil or cs[f2] ~= nil or cs[f3] ~= nil end if fragment_count == 4 then local f1, f2, f3, f4 = ... - return (m_has_p and (has_f(self, f1) or has_f(self, f2) or has_f(self, f3) or has_f(self, f4))) - or (not m_has_p and (cs[f1] ~= nil or cs[f2] ~= nil or cs[f3] ~= nil or cs[f4] ~= nil)) + return cs[f1] ~= nil or cs[f2] ~= nil or cs[f3] ~= nil or cs[f4] ~= nil end do local f1, f2, f3, f4 = ... - return (m_has_p and (has_f(self, f1) or has_f(self, f2) or has_f(self, f3) or has_f(self, f4) - or has_fs(self, __lua_select(5, ...)))) - or (not m_has_p and (cs[f1] ~= nil or cs[f2] ~= nil or cs[f3] ~= nil or cs[f4] ~= nil - or has_fs(self, __lua_select(5, ...)))) + return cs[f1] ~= nil or cs[f2] ~= nil or cs[f3] ~= nil or cs[f4] ~= nil + or self:has_any(__lua_select(5, ...)) end end @@ -6986,42 +5583,19 @@ end ---@param component evolved.component ---@return evolved.builder builder function __builder_mt:set(fragment, component) - local fragment_primary, fragment_secondary, fragment_options = - __evolved_unpack(fragment) + local fragment_primary, _ = __evolved_unpack(fragment) if __debug_mode then - if fragment_options < __PAIR_OPTIONS then - if fragment_primary == __ANY_INDEX then - __error_fmt('the id (%s) is a wildcard and cannot be set', - __universal_name(fragment)) - elseif __freelist_ids[fragment_primary] ~= fragment then - __error_fmt('the id (%s) is not alive and cannot be set', - __universal_name(fragment)) - end - else - if fragment_options >= __PRI_WILDCARD_OPTIONS then - __error_fmt('the pair (%s) is a wildcard and cannot be set', - __universal_name(fragment)) - end - - local fragment_primary_id = __freelist_ids[fragment_primary] --[[@as evolved.id?]] - if not fragment_primary_id or fragment_primary_id % 2 ^ 20 ~= fragment_primary then - __error_fmt('the pair (%s) has no alive primary id and cannot be set', - __universal_name(fragment)) - end - - local fragment_secondary_id = __freelist_ids[fragment_secondary] --[[@as evolved.id?]] - if not fragment_secondary_id or fragment_secondary_id % 2 ^ 20 ~= fragment_secondary then - __error_fmt('the pair (%s) has no alive secondary id and cannot be set', - __universal_name(fragment)) - end + if __freelist_ids[fragment_primary] ~= fragment then + __error_fmt('the id (%s) is not alive and cannot be set', + __universal_name(fragment)) end end do ---@type evolved.default?, evolved.duplicate? local fragment_default, fragment_duplicate = - __primary_get(fragment, __DEFAULT, __DUPLICATE) + __evolved_get(fragment, __DEFAULT, __DUPLICATE) local new_component = component if new_component == nil then new_component = fragment_default end @@ -7031,39 +5605,6 @@ function __builder_mt:set(fragment, component) self.__components[fragment] = new_component end - if fragment_options >= __PAIR_OPTIONS then - local primary_pairs = self.__primary_pairs - local secondary_pairs = self.__secondary_pairs - - if not primary_pairs then - primary_pairs = {} - self.__primary_pairs = primary_pairs - end - - if not secondary_pairs then - secondary_pairs = {} - self.__secondary_pairs = secondary_pairs - end - - local primary_fragments = primary_pairs[fragment_primary] - local secondary_fragments = secondary_pairs[fragment_secondary] - - if not primary_fragments then - ---@type evolved.assoc_list - primary_fragments = __assoc_list_new(4) - primary_pairs[fragment_primary] = primary_fragments - end - - if not secondary_fragments then - ---@type evolved.assoc_list - secondary_fragments = __assoc_list_new(4) - secondary_pairs[fragment_secondary] = secondary_fragments - end - - __assoc_list_insert(primary_fragments, fragment) - __assoc_list_insert(secondary_fragments, fragment) - end - return self end @@ -7076,100 +5617,42 @@ function __builder_mt:remove(...) return self end - local fragment = ... + local cs = self.__components - local components = self.__components - - components[fragment] = nil - - local primary_pairs = self.__primary_pairs - local secondary_pairs = self.__secondary_pairs - - local maybe_has_pairs = primary_pairs and secondary_pairs - - if maybe_has_pairs and fragment >= __PRI_WILDCARD_OPTIONS * 2 ^ 40 then - ---@cast primary_pairs -? - ---@cast secondary_pairs -? - - local fragment_primary, fragment_secondary, fragment_options = - __evolved_unpack(fragment) - - if fragment_options == __ANY_WILDCARD_OPTIONS then - for primary_index, primary_fragments in __lua_next, primary_pairs do - local primary_fragment_list = primary_fragments.__item_list - local primary_fragment_count = primary_fragments.__item_count - - for primary_fragment_index = 1, primary_fragment_count do - local primary_fragment = primary_fragment_list[primary_fragment_index] - components[primary_fragment] = nil - end - - primary_pairs[primary_index] = nil - end - - for secondary_index, secondary_fragments in __lua_next, secondary_pairs do - local secondary_fragment_list = secondary_fragments.__item_list - local secondary_fragment_count = secondary_fragments.__item_count - - for secondary_fragment_index = 1, secondary_fragment_count do - local secondary_fragment = secondary_fragment_list[secondary_fragment_index] - components[secondary_fragment] = nil - end - - secondary_pairs[secondary_index] = nil - end - elseif fragment_options == __PRI_WILDCARD_OPTIONS then - local secondary_fragments = secondary_pairs[fragment_secondary] - local secondary_fragment_list = secondary_fragments and secondary_fragments.__item_list - local secondary_fragment_count = secondary_fragments and secondary_fragments.__item_count or 0 - - for secondary_fragment_index = 1, secondary_fragment_count do - local secondary_fragment = secondary_fragment_list[secondary_fragment_index] - components[secondary_fragment] = nil - - local secondary_fragment_primary_index, _ = __evolved_unpack(secondary_fragment) - if __assoc_list_remove(primary_pairs[secondary_fragment_primary_index], secondary_fragment) == 0 then - primary_pairs[secondary_fragment_primary_index] = nil - end - end - - secondary_pairs[fragment_secondary] = nil - elseif fragment_options == __SEC_WILDCARD_OPTIONS then - local primary_fragments = primary_pairs[fragment_primary] - local primary_fragment_list = primary_fragments and primary_fragments.__item_list - local primary_fragment_count = primary_fragments and primary_fragments.__item_count or 0 - - for primary_fragment_index = 1, primary_fragment_count do - local primary_fragment = primary_fragment_list[primary_fragment_index] - components[primary_fragment] = nil - - local _, primary_fragment_secondary_index = __evolved_unpack(primary_fragment) - if __assoc_list_remove(secondary_pairs[primary_fragment_secondary_index], primary_fragment) == 0 then - secondary_pairs[primary_fragment_secondary_index] = nil - end - end - - primary_pairs[fragment_primary] = nil - end + if fragment_count == 1 then + local f1 = ... + cs[f1] = nil + return self end - return fragment_count > 1 and self:remove(__lua_select(2, ...)) or self + if fragment_count == 2 then + local f1, f2 = ... + cs[f1], cs[f2] = nil, nil + return self + end + + if fragment_count == 3 then + local f1, f2, f3 = ... + cs[f1], cs[f2], cs[f3] = nil, nil, nil + return self + end + + if fragment_count == 4 then + local f1, f2, f3, f4 = ... + cs[f1], cs[f2], cs[f3], cs[f4] = nil, nil, nil, nil + return self + end + + do + local f1, f2, f3, f4 = ... + cs[f1], cs[f2], cs[f3], cs[f4] = nil, nil, nil, nil + return self:remove(__lua_select(5, ...)) + end end ---@return evolved.builder builder function __builder_mt:clear() - if self.__components then - __lua_table_clear(self.__components) - end - - if self.__primary_pairs then - __lua_table_clear(self.__primary_pairs) - end - - if self.__secondary_pairs then - __lua_table_clear(self.__secondary_pairs) - end - + __lua_table_clear(self.__components) return self end @@ -7405,8 +5888,6 @@ __evolved_set(__REQUIRES, __ON_REMOVE, __update_major_chunks_hook) --- --- -__evolved_set(__ANY, __NAME, 'ANY') - __evolved_set(__TAG, __NAME, 'TAG') __evolved_set(__NAME, __NAME, 'NAME') @@ -7447,8 +5928,6 @@ __evolved_set(__DESTRUCTION_POLICY_REMOVE_FRAGMENT, __NAME, 'DESTRUCTION_POLICY_ --- --- -__evolved_set(__ANY, __INTERNAL) - __evolved_set(__TAG, __INTERNAL) __evolved_set(__NAME, __INTERNAL) @@ -7489,8 +5968,6 @@ __evolved_set(__DESTRUCTION_POLICY_REMOVE_FRAGMENT, __INTERNAL) --- --- -__evolved_set(__ANY, __TAG) - __evolved_set(__TAG, __TAG) __evolved_set(__UNIQUE, __TAG) @@ -7659,8 +6136,6 @@ end) --- --- -evolved.ANY = __ANY - evolved.TAG = __TAG evolved.NAME = __NAME @@ -7697,7 +6172,7 @@ evolved.DESTRUCTION_POLICY_REMOVE_FRAGMENT = __DESTRUCTION_POLICY_REMOVE_FRAGMEN --- --- ---- Core Functions +--- Functions --- --- @@ -7748,27 +6223,6 @@ evolved.collect_garbage = __evolved_collect_garbage evolved.chunk = __evolved_chunk evolved.builder = __evolved_builder ---- ---- ---- Relation Functions ---- ---- - -evolved.pair = __evolved_pair -evolved.unpair = __evolved_unpair - -evolved.is_pair = __evolved_is_pair -evolved.is_wildcard = __evolved_is_wildcard - -evolved.primary = __evolved_primary -evolved.secondary = __evolved_secondary - -evolved.primaries = __evolved_primaries -evolved.secondaries = __evolved_secondaries - -evolved.primary_count = __evolved_primary_count -evolved.secondary_count = __evolved_secondary_count - --- --- ---