From e70781d463403dfc6b921959ec162c2348c9d49a Mon Sep 17 00:00:00 2001 From: BlackMATov Date: Tue, 7 Jan 2025 08:18:42 +0700 Subject: [PATCH] first builders impl (without optimization yet) --- README.md | 21 ++++ develop/untests.lua | 58 ++++++++++ evolved.lua | 261 +++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 339 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index ceb27a0..1377bec 100644 --- a/README.md +++ b/README.md @@ -75,4 +75,25 @@ each :: entity -> {each_state? -> fragment?, component?}, each_state? execute :: query -> {execute_state? -> chunk?, entity[]?}, execute_state? ``` +``` +entity :: entity_builder +entity_builder:set :: fragment, any... -> entity_builder +entity_builder:build :: entity +``` + +``` +fragment :: fragment_builder +fragment_builder:tag :: fragment_builder +fragment_builder:default :: component -> fragment_builder +fragment_builder:construct :: {any... -> component} -> fragment_builder +fragment_builder:build :: fragment +``` + +``` +query :: query_builder +query_builder:include :: fragment... -> query_builder +query_builder:exclude :: fragment... -> query_builder +query_builder:build :: query +``` + ## [License (MIT)](./LICENSE.md) diff --git a/develop/untests.lua b/develop/untests.lua index cac1a4e..8f776dd 100644 --- a/develop/untests.lua +++ b/develop/untests.lua @@ -2453,3 +2453,61 @@ do end end end + +do + local f1 = evo.fragment():default(41):build() + local f2 = evo.fragment():construct(function() return 42 end):build() + local f3 = evo.fragment():tag():build() + + local e0 = evo.entity():build() + assert(not evo.has_any(e0, f1, f2, f3)) + + local e1 = evo.entity():set(f1):build() + assert(evo.has(e1, f1)) + assert(evo.get(e1, f1) == 41) + + local e2 = evo.entity():set(f1):set(f2):build() + assert(evo.has(e2, f1) and evo.has(e2, f2)) + assert(evo.get(e2, f1) == 41 and evo.get(e2, f2) == 42) + + local e3 = evo.entity():set(f1):set(f2):set(f3):build() + assert(evo.has(e3, f1) and evo.has(e3, f2) and evo.has(e3, f3)) + assert(evo.get(e3, f1) == 41 and evo.get(e3, f2) == 42 and evo.get(e3, f3) == nil) + + ---@param q evolved.query + ---@return evolved.entity[] + local function collect_entities(q) + local entities = {} + for _, es in evo.execute(q) do + for _, e in ipairs(es) do + entities[#entities + 1] = e + end + end + return entities + end + + local q1 = evo.query():include(f1):build() + local q2 = evo.query():include(f1, f2):build() + local q3 = evo.query():include(f1):include(f2):exclude(f3):build() + + do + local entities = collect_entities(q1) + assert(#entities == 3) + assert(entities[1] == e1) + assert(entities[2] == e2) + assert(entities[3] == e3) + end + + do + local entities = collect_entities(q2) + assert(#entities == 2) + assert(entities[1] == e2) + assert(entities[2] == e3) + end + + do + local entities = collect_entities(q3) + assert(#entities == 1) + assert(entities[1] == e2) + end +end diff --git a/evolved.lua b/evolved.lua index 453fd40..9eafeb2 100644 --- a/evolved.lua +++ b/evolved.lua @@ -219,9 +219,10 @@ local __TABLE_POOL_TAG__CHUNK_LIST = 2 local __TABLE_POOL_TAG__EACH_STATE = 3 local __TABLE_POOL_TAG__EXECUTE_STATE = 4 local __TABLE_POOL_TAG__FRAGMENT_LIST = 5 +local __TABLE_POOL_TAG__COMPONENT_LIST = 6 ---@type table -local __tagged_table_pools = __table_new(5, 0) +local __tagged_table_pools = __table_new(6, 0) ---@param tag evolved.table_pool_tag ---@param narray integer @@ -2756,4 +2757,262 @@ end --- --- +---@class (exact) evolved.__entity_builder +---@field package __fragment_list? evolved.fragment[] +---@field package __component_list? evolved.component[] + +---@class evolved.entity_builder : evolved.__entity_builder +local evolved_entity_builder = {} +evolved_entity_builder.__index = evolved_entity_builder + +---@return evolved.entity_builder +---@nodiscard +function evolved.entity() + ---@type evolved.__entity_builder + local builder = { + __fragment_list = nil, + __component_list = nil, + } + ---@cast builder evolved.entity_builder + return setmetatable(builder, evolved_entity_builder) +end + +---@param fragment evolved.fragment +---@param ... any component arguments +---@return evolved.entity_builder +function evolved_entity_builder:set(fragment, ...) + local component = __component_construct(fragment, ...) + + local fragment_list = self.__fragment_list + local component_list = self.__component_list + + if not fragment_list then + fragment_list = __acquire_table(__TABLE_POOL_TAG__FRAGMENT_LIST, 8, 0) + self.__fragment_list = fragment_list + end + + if not component_list then + component_list = __acquire_table(__TABLE_POOL_TAG__COMPONENT_LIST, 8, 0) + self.__component_list = component_list + end + + fragment_list[#fragment_list + 1] = fragment + component_list[#component_list + 1] = component + + return self +end + +---@return evolved.entity +function evolved_entity_builder:build() + local fragment_list = self.__fragment_list + local component_list = self.__component_list + + self.__fragment_list = nil + self.__component_list = nil + + local entity = evolved.id() + + if fragment_list and component_list then + for i = 1, #fragment_list do + local fragment = fragment_list[i] + local component = component_list[i] + evolved.set(entity, fragment, component) + end + end + + if fragment_list then + __release_table(__TABLE_POOL_TAG__FRAGMENT_LIST, fragment_list) + end + + if component_list then + __release_table(__TABLE_POOL_TAG__COMPONENT_LIST, component_list) + end + + return entity +end + +--- +--- +--- +--- +--- + +---@class (evact) evolved.__fragment_builder +---@field package __tag boolean +---@field package __default? evolved.component +---@field package __construct? fun(...): evolved.component + +---@class evolved.fragment_builder : evolved.__fragment_builder +local evolved_fragment_builder = {} +evolved_fragment_builder.__index = evolved_fragment_builder + +---@return evolved.fragment_builder +---@nodiscard +function evolved.fragment() + ---@type evolved.__fragment_builder + local builder = { + __tag = false, + __default = nil, + __construct = nil, + } + ---@cast builder evolved.fragment_builder + return setmetatable(builder, evolved_fragment_builder) +end + +---@return evolved.fragment_builder +function evolved_fragment_builder:tag() + self.__tag = true + return self +end + +---@param default evolved.component +---@return evolved.fragment_builder +function evolved_fragment_builder:default(default) + self.__default = default + return self +end + +---@param construct fun(...): evolved.component +---@return evolved.fragment_builder +function evolved_fragment_builder:construct(construct) + self.__construct = construct + return self +end + +---@return evolved.fragment +function evolved_fragment_builder:build() + local tag = self.__tag + local default = self.__default + local construct = self.__construct + + self.__tag = false + self.__default = nil + self.__construct = nil + + local fragment = evolved.id() + + if tag then + evolved.set(fragment, evolved.TAG, tag) + end + + if default ~= nil then + evolved.set(fragment, evolved.DEFAULT, default) + end + + if construct ~= nil then + evolved.set(fragment, evolved.CONSTRUCT, construct) + end + + return fragment +end + +--- +--- +--- +--- +--- + +---@class (exact) evolved.__query_builder +---@field package __include_list? evolved.fragment[] +---@field package __exclude_list? evolved.fragment[] + +---@class evolved.query_builder : evolved.__query_builder +local evolved_query_builder = {} +evolved_query_builder.__index = evolved_query_builder + +---@return evolved.query_builder +---@nodiscard +function evolved.query() + ---@type evolved.__query_builder + local builder = { + __include_list = nil, + __exclude_list = nil, + } + ---@cast builder evolved.query_builder + return setmetatable(builder, evolved_query_builder) +end + +---@param ... evolved.fragment fragments +---@return evolved.query_builder +function evolved_query_builder:include(...) + local fragment_count = select('#', ...) + + if fragment_count == 0 then + return self + end + + local include_list = self.__include_list + + if not include_list then + include_list = __acquire_table(__TABLE_POOL_TAG__FRAGMENT_LIST, 8, 0) + self.__include_list = include_list + end + + local include_list_size = #include_list + + for i = 1, fragment_count do + local fragment = select(i, ...) + include_list[include_list_size + 1] = fragment + include_list_size = include_list_size + 1 + end + + return self +end + +---@param ... evolved.fragment fragments +---@return evolved.query_builder +function evolved_query_builder:exclude(...) + local fragment_count = select('#', ...) + + if fragment_count == 0 then + return self + end + + local exclude_list = self.__exclude_list + + if not exclude_list then + exclude_list = __acquire_table(__TABLE_POOL_TAG__FRAGMENT_LIST, 8, 0) + self.__exclude_list = exclude_list + end + + local exclude_list_size = #exclude_list + + for i = 1, fragment_count do + local fragment = select(i, ...) + exclude_list[exclude_list_size + 1] = fragment + exclude_list_size = exclude_list_size + 1 + end + + return self +end + +---@return evolved.query +function evolved_query_builder:build() + local include_list = self.__include_list + local exclude_list = self.__exclude_list + + self.__include_list = nil + self.__exclude_list = nil + + local query = evolved.id() + + if include_list then + evolved.insert(query, evolved.INCLUDES, __table_unpack(include_list)) + __release_table(__TABLE_POOL_TAG__FRAGMENT_LIST, include_list) + end + + if exclude_list then + evolved.insert(query, evolved.EXCLUDES, __table_unpack(exclude_list)) + __release_table(__TABLE_POOL_TAG__FRAGMENT_LIST, exclude_list) + end + + return query +end + +--- +--- +--- +--- +--- + return evolved