diff --git a/README.md b/README.md index 873f6c7..2c3d1e6 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,8 @@ registry.remove -> entity -> entity -> () registry.query -> entity -> entity... -> (query) registry.execute -> query -> (() -> (chunk?)) registry.chunk -> entity -> entity... -> (chunk) +registry.chunk_entities -> chunk -> entity -> (entity[]) +registry.chunk_components -> chunk -> entity -> (any[]) ``` ### Module `singles` diff --git a/ROADMAP.md b/ROADMAP.md index 7e5b415..1612995 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -2,7 +2,7 @@ ## Backlog -- add oop-like api for entities and queries -- cache matched chunks in queries -- cache transitions between chunks -- chunk's children should be sorted by id and stored in an array instead of a table \ No newline at end of file +- [ ] add oop-like api for entities and queries +- [ ] cache matched chunks in queries +- [ ] cache transitions between chunks +- [ ] chunk's children should be sorted by id and stored in an array instead of a table \ No newline at end of file diff --git a/develop/example.lua b/develop/example.lua index 8de1466..6819c90 100644 --- a/develop/example.lua +++ b/develop/example.lua @@ -27,10 +27,11 @@ do local dt = evo.singles.get(singles.delta_time) for chunk in evo.registry.execute(queries.bodies) do - local ps = chunk.components[fragments.position] - local vs = chunk.components[fragments.velocity] + local es = evo.registry.chunk_entities(chunk) + local ps = evo.registry.chunk_components(chunk, fragments.position) + local vs = evo.registry.chunk_components(chunk, fragments.velocity) - for i = 1, #chunk.entities do + for i = 1, #es do ps[i] = ps[i] + vs[i] * dt end end diff --git a/develop/untests/idpools_untests.lua b/develop/untests/idpools_untests.lua index ff0e23b..63ab751 100644 --- a/develop/untests/idpools_untests.lua +++ b/develop/untests/idpools_untests.lua @@ -1,3 +1,4 @@ +---@diagnostic disable: invisible local evo = require 'evolved.evolved' do diff --git a/develop/untests/registry_untests.lua b/develop/untests/registry_untests.lua index debe668..376fabb 100644 --- a/develop/untests/registry_untests.lua +++ b/develop/untests/registry_untests.lua @@ -1,10 +1,11 @@ +---@diagnostic disable: invisible local evo = require 'evolved.evolved' do local f1, f2 = evo.registry.entity(), evo.registry.entity() local e = evo.registry.entity() - assert(e.chunk == nil) + assert(e.__chunk == nil) evo.registry.insert(e, f1) assert(evo.registry.has(e, f1)) @@ -57,45 +58,45 @@ do local e = evo.registry.entity() - evo.registry.insert(e, f1, f1.guid) - assert(e.chunk == evo.registry.chunk(f1)) + evo.registry.insert(e, f1, f1.__guid) + assert(e.__chunk == evo.registry.chunk(f1)) do local chunk_f1 = evo.registry.chunk(f1) - assert(#chunk_f1.entities == 1) - assert(#chunk_f1.components[f1] == 1) + assert(#chunk_f1.__entities == 1) + assert(#chunk_f1.__components[f1] == 1) end - evo.registry.insert(e, f2, f2.guid) - assert(e.chunk == evo.registry.chunk(f1, f2)) + evo.registry.insert(e, f2, f2.__guid) + assert(e.__chunk == evo.registry.chunk(f1, f2)) do local chunk_f1 = evo.registry.chunk(f1) - assert(#chunk_f1.entities == 0) - assert(#chunk_f1.components[f1] == 0) + assert(#chunk_f1.__entities == 0) + assert(#chunk_f1.__components[f1] == 0) local chunk_f1_f2 = evo.registry.chunk(f1, f2) - assert(#chunk_f1_f2.entities == 1) - assert(#chunk_f1_f2.components[f1] == 1) - assert(#chunk_f1_f2.components[f2] == 1) + assert(#chunk_f1_f2.__entities == 1) + assert(#chunk_f1_f2.__components[f1] == 1) + assert(#chunk_f1_f2.__components[f2] == 1) end evo.registry.remove(e, f1) - assert(e.chunk == evo.registry.chunk(f2)) + assert(e.__chunk == evo.registry.chunk(f2)) do local chunk_f1 = evo.registry.chunk(f1) - assert(#chunk_f1.entities == 0) - assert(#chunk_f1.components[f1] == 0) + assert(#chunk_f1.__entities == 0) + assert(#chunk_f1.__components[f1] == 0) local chunk_f2 = evo.registry.chunk(f2) - assert(#chunk_f2.entities == 1) - assert(#chunk_f2.components[f2] == 1) + assert(#chunk_f2.__entities == 1) + assert(#chunk_f2.__components[f2] == 1) local chunk_f1_f2 = evo.registry.chunk(f1, f2) - assert(#chunk_f1_f2.entities == 0) - assert(#chunk_f1_f2.components[f1] == 0) - assert(#chunk_f1_f2.components[f2] == 0) + assert(#chunk_f1_f2.__entities == 0) + assert(#chunk_f1_f2.__components[f1] == 0) + assert(#chunk_f1_f2.__components[f2] == 0) end end @@ -133,15 +134,15 @@ for _ = 1, 100 do shuffle_array(insert_fragments) for _, f in ipairs(insert_fragments) do - evo.registry.insert(e1, f, f.guid) + evo.registry.insert(e1, f, f.__guid) end shuffle_array(insert_fragments) for _, f in ipairs(insert_fragments) do - evo.registry.insert(e2, f, f.guid) + evo.registry.insert(e2, f, f.__guid) end - assert(e1.chunk == e2.chunk) + assert(e1.__chunk == e2.__chunk) assert(evo.registry.has_all(e1, evo.compat.unpack(insert_fragments))) assert(evo.registry.has_all(e2, evo.compat.unpack(insert_fragments))) @@ -159,14 +160,14 @@ for _ = 1, 100 do end end - assert(e1.chunk == e2.chunk) + assert(e1.__chunk == e2.__chunk) assert(not evo.registry.has_any(e1, evo.compat.unpack(remove_fragments))) assert(not evo.registry.has_any(e2, evo.compat.unpack(remove_fragments))) - if e1.chunk ~= nil then - for f, _ in pairs(e1.chunk.components) do - assert(evo.registry.get(e1, f) == f.guid) - assert(evo.registry.get(e2, f) == f.guid) + if e1.__chunk ~= nil then + for f, _ in pairs(e1.__chunk.__components) do + assert(evo.registry.get(e1, f) == f.__guid) + assert(evo.registry.get(e2, f) == f.__guid) end end end @@ -216,7 +217,7 @@ do local function collect_query_entities(query) local entities = {} ---@type evolved.entity[] for chunk in evo.registry.execute(query) do - for _, e in ipairs(chunk.entities) do + for _, e in ipairs(chunk.__entities) do table.insert(entities, e) end end diff --git a/evolved/idpools.lua b/evolved/idpools.lua index 2e5f0fb..1cc4316 100644 --- a/evolved/idpools.lua +++ b/evolved/idpools.lua @@ -2,8 +2,8 @@ local idpools = {} ---@class evolved.idpool ----@field acquired_ids integer[] ----@field available_index integer +---@field package __acquired_ids integer[] +---@field package __available_index integer local evolved_idpool_mt = {} evolved_idpool_mt.__index = evolved_idpool_mt @@ -11,8 +11,8 @@ evolved_idpool_mt.__index = evolved_idpool_mt function idpools.idpool() ---@type evolved.idpool local idpool = { - acquired_ids = {}, - available_index = 0, + __acquired_ids = {}, + __available_index = 0, } return setmetatable(idpool, evolved_idpool_mt) end @@ -30,24 +30,24 @@ end ---@return integer ---@nodiscard function idpools.acquire_id(idpool) - if idpool.available_index ~= 0 then - local index = idpool.available_index - local available_id = idpool.acquired_ids[index] - idpool.available_index = available_id % 0x100000 - local version = available_id - idpool.available_index + if idpool.__available_index ~= 0 then + local index = idpool.__available_index + local available_id = idpool.__acquired_ids[index] + idpool.__available_index = available_id % 0x100000 + local version = available_id - idpool.__available_index local acquired_id = index + version - idpool.acquired_ids[index] = acquired_id + idpool.__acquired_ids[index] = acquired_id return acquired_id end - if #idpool.acquired_ids == 0xFFFFF then + if #idpool.__acquired_ids == 0xFFFFF then error('id index overflow', 2) end - local index = #idpool.acquired_ids + 1 + local index = #idpool.__acquired_ids + 1 local version = 0x100000 local acquired_id = index + version - idpool.acquired_ids[index] = acquired_id + idpool.__acquired_ids[index] = acquired_id return acquired_id end @@ -57,7 +57,7 @@ function idpools.release_id(idpool, id) local index = id % 0x100000 local version = id - index - if idpool.acquired_ids[index] ~= id then + if idpool.__acquired_ids[index] ~= id then error('id is not acquired or already released', 2) end @@ -65,8 +65,8 @@ function idpools.release_id(idpool, id) and 0x100000 or version + 0x100000 - idpool.acquired_ids[index] = idpool.available_index + version - idpool.available_index = index + idpool.__acquired_ids[index] = idpool.__available_index + version + idpool.__available_index = index end ---@param idpool evolved.idpool @@ -75,7 +75,7 @@ end ---@nodiscard function idpools.is_id_alive(idpool, id) local index = id % 0x100000 - return idpool.acquired_ids[index] == id + return idpool.__acquired_ids[index] == id end return idpools diff --git a/evolved/registry.lua b/evolved/registry.lua index 79aeb9c..3c8a0bd 100644 --- a/evolved/registry.lua +++ b/evolved/registry.lua @@ -22,23 +22,23 @@ local __queries = {} ---@type table --- ---@class evolved.entity ----@field guid integer ----@field chunk? evolved.chunk ----@field index_in_chunk integer +---@field package __guid integer +---@field package __chunk? evolved.chunk +---@field package __index_in_chunk integer local evolved_entity_mt = {} evolved_entity_mt.__index = evolved_entity_mt ---@class evolved.query ----@field fragments evolved.entity[] +---@field package __fragments evolved.entity[] local evolved_query_mt = {} evolved_query_mt.__index = evolved_query_mt ---@class evolved.chunk ----@field parent? evolved.chunk ----@field fragment evolved.entity ----@field children table ----@field entities evolved.entity[] ----@field components table +---@field package __parent? evolved.chunk +---@field package __fragment evolved.entity +---@field package __children table +---@field package __entities evolved.entity[] +---@field package __components table local evolved_chunk_mt = {} evolved_chunk_mt.__index = evolved_chunk_mt @@ -50,7 +50,7 @@ evolved_chunk_mt.__index = evolved_chunk_mt ---@param query evolved.query local function __on_new_query(query) - local main_fragment = query.fragments[#query.fragments] + local main_fragment = query.__fragments[#query.__fragments] local main_fragment_queries = __queries[main_fragment] or {} main_fragment_queries[#main_fragment_queries + 1] = query __queries[main_fragment] = main_fragment_queries @@ -58,7 +58,7 @@ end ---@param chunk evolved.chunk local function __on_new_chunk(chunk) - local main_fragment = chunk.fragment + local main_fragment = chunk.__fragment local main_fragment_chunks = __chunks[main_fragment] or {} main_fragment_chunks[#main_fragment_chunks + 1] = chunk __chunks[main_fragment] = main_fragment_chunks @@ -72,28 +72,28 @@ end ---@param entity evolved.entity local function __detach_entity(entity) - local chunk = assert(entity.chunk) - local index_in_chunk = entity.index_in_chunk + local chunk = assert(entity.__chunk) + local index_in_chunk = entity.__index_in_chunk - if index_in_chunk == #chunk.entities then - chunk.entities[index_in_chunk] = nil + if index_in_chunk == #chunk.__entities then + chunk.__entities[index_in_chunk] = nil - for _, cs in pairs(chunk.components) do + for _, cs in pairs(chunk.__components) do cs[index_in_chunk] = nil end else - chunk.entities[index_in_chunk] = chunk.entities[#chunk.entities] - chunk.entities[index_in_chunk].index_in_chunk = index_in_chunk - chunk.entities[#chunk.entities] = nil + chunk.__entities[index_in_chunk] = chunk.__entities[#chunk.__entities] + chunk.__entities[index_in_chunk].__index_in_chunk = index_in_chunk + chunk.__entities[#chunk.__entities] = nil - for _, cs in pairs(chunk.components) do + for _, cs in pairs(chunk.__components) do cs[index_in_chunk] = cs[#cs] cs[#cs] = nil end end - entity.chunk = nil - entity.index_in_chunk = 0 + entity.__chunk = nil + entity.__index_in_chunk = 0 end ---@param chunk evolved.chunk @@ -101,7 +101,7 @@ end ---@return boolean ---@nodiscard local function __chunk_has_fragment(chunk, fragment) - return chunk.components[fragment] ~= nil + return chunk.__components[fragment] ~= nil end ---@param chunk evolved.chunk @@ -110,7 +110,7 @@ end ---@return boolean ---@nodiscard local function __chunk_has_all_fragments(chunk, fragment, ...) - local components = chunk.components + local components = chunk.__components if components[fragment] == nil then return false @@ -131,7 +131,7 @@ end ---@return boolean ---@nodiscard local function __chunk_has_any_fragments(chunk, fragment, ...) - local components = chunk.components + local components = chunk.__components if components[fragment] ~= nil then return true @@ -157,11 +157,11 @@ local function __root_chunk(fragment) ---@type evolved.chunk local root_chunk = { - parent = nil, - fragment = fragment, - children = {}, - entities = {}, - components = { [fragment] = {} }, + __parent = nil, + __fragment = fragment, + __children = {}, + __entities = {}, + __components = { [fragment] = {} }, } setmetatable(root_chunk, evolved_chunk_mt) @@ -183,37 +183,37 @@ local function __chunk_with_fragment(chunk, fragment) return __root_chunk(fragment) end - if fragment.guid == chunk.fragment.guid then + if fragment.__guid == chunk.__fragment.__guid then return chunk end - if fragment.guid < chunk.fragment.guid then - local sibling_chunk = __chunk_with_fragment(chunk.parent, fragment) - return __chunk_with_fragment(sibling_chunk, chunk.fragment) + if fragment.__guid < chunk.__fragment.__guid then + local sibling_chunk = __chunk_with_fragment(chunk.__parent, fragment) + return __chunk_with_fragment(sibling_chunk, chunk.__fragment) end do - local child_chunk = chunk.children[fragment] + local child_chunk = chunk.__children[fragment] if child_chunk then return child_chunk end end ---@type evolved.chunk local child_chunk = { - parent = chunk, - fragment = fragment, - children = {}, - entities = {}, - components = { [fragment] = {} }, + __parent = chunk, + __fragment = fragment, + __children = {}, + __entities = {}, + __components = { [fragment] = {} }, } - for f, _ in pairs(chunk.components) do - child_chunk.components[f] = {} + for f, _ in pairs(chunk.__components) do + child_chunk.__components[f] = {} end setmetatable(child_chunk, evolved_chunk_mt) do - chunk.children[fragment] = child_chunk + chunk.__children[fragment] = child_chunk end __on_new_chunk(child_chunk) @@ -229,13 +229,13 @@ local function __chunk_without_fragment(chunk, fragment) return nil end - if fragment.guid == chunk.fragment.guid then - return chunk.parent + if fragment.__guid == chunk.__fragment.__guid then + return chunk.__parent end - if fragment.guid < chunk.fragment.guid then - local sibling_chunk = __chunk_without_fragment(chunk.parent, fragment) - return __chunk_with_fragment(sibling_chunk, chunk.fragment) + if fragment.__guid < chunk.__fragment.__guid then + local sibling_chunk = __chunk_without_fragment(chunk.__parent, fragment) + return __chunk_with_fragment(sibling_chunk, chunk.__fragment) end return chunk @@ -254,9 +254,9 @@ function registry.entity() ---@type evolved.entity local entity = { - guid = guid, - chunk = nil, - index_in_chunk = 0, + __guid = guid, + __chunk = nil, + __index_in_chunk = 0, } return setmetatable(entity, evolved_entity_mt) @@ -266,7 +266,7 @@ end ---@return boolean ---@nodiscard function registry.is_alive(entity) - return idpools.is_id_alive(__guids, entity.guid) + return idpools.is_id_alive(__guids, entity.__guid) end ---@param entity evolved.entity @@ -275,11 +275,11 @@ function registry.destroy(entity) error(string.format('entity %s is not alive', entity), 2) end - if entity.chunk ~= nil then + if entity.__chunk ~= nil then __detach_entity(entity) end - idpools.release_id(__guids, entity.guid) + idpools.release_id(__guids, entity.__guid) end ---@param entity evolved.entity @@ -287,13 +287,13 @@ end ---@return any ---@nodiscard function registry.get(entity, fragment) - local chunk_components = entity.chunk and entity.chunk.components[fragment] + local chunk_components = entity.__chunk and entity.__chunk.__components[fragment] if chunk_components == nil then error(string.format('entity %s does not have fragment %s', entity, fragment), 2) end - return chunk_components[entity.index_in_chunk] + return chunk_components[entity.__index_in_chunk] end ---@param entity evolved.entity @@ -302,13 +302,13 @@ end ---@return any ---@nodiscard function registry.get_or(entity, fragment, default) - local chunk_components = entity.chunk and entity.chunk.components[fragment] + local chunk_components = entity.__chunk and entity.__chunk.__components[fragment] if chunk_components == nil then return default end - return chunk_components[entity.index_in_chunk] + return chunk_components[entity.__index_in_chunk] end ---@param entity evolved.entity @@ -316,7 +316,7 @@ end ---@return boolean ---@nodiscard function registry.has(entity, fragment) - return entity.chunk ~= nil and __chunk_has_fragment(entity.chunk, fragment) + return entity.__chunk ~= nil and __chunk_has_fragment(entity.__chunk, fragment) end ---@param entity evolved.entity @@ -325,7 +325,7 @@ end ---@return boolean ---@nodiscard function registry.has_all(entity, fragment, ...) - return entity.chunk ~= nil and __chunk_has_all_fragments(entity.chunk, fragment, ...) + return entity.__chunk ~= nil and __chunk_has_all_fragments(entity.__chunk, fragment, ...) end ---@param entity evolved.entity @@ -334,7 +334,7 @@ end ---@return boolean ---@nodiscard function registry.has_any(entity, fragment, ...) - return entity.chunk ~= nil and __chunk_has_any_fragments(entity.chunk, fragment, ...) + return entity.__chunk ~= nil and __chunk_has_any_fragments(entity.__chunk, fragment, ...) end ---@param entity evolved.entity @@ -343,13 +343,13 @@ end function registry.assign(entity, fragment, component) component = component == nil and true or component - local chunk_components = entity.chunk and entity.chunk.components[fragment] + local chunk_components = entity.__chunk and entity.__chunk.__components[fragment] if chunk_components == nil then error(string.format('entity %s does not have fragment %s', entity, fragment), 2) end - chunk_components[entity.index_in_chunk] = component + chunk_components[entity.__index_in_chunk] = component end ---@param entity evolved.entity @@ -358,23 +358,23 @@ end function registry.insert(entity, fragment, component) component = component == nil and true or component - local old_chunk = entity.chunk + local old_chunk = entity.__chunk local new_chunk = __chunk_with_fragment(old_chunk, fragment) if old_chunk == new_chunk then error(string.format('entity %s already has fragment %s', entity, fragment), 2) end - local old_index_in_chunk = entity.index_in_chunk - local new_index_in_chunk = new_chunk and #new_chunk.entities + 1 or 0 + local old_index_in_chunk = entity.__index_in_chunk + local new_index_in_chunk = new_chunk and #new_chunk.__entities + 1 or 0 if new_chunk ~= nil then - new_chunk.entities[new_index_in_chunk] = entity - new_chunk.components[fragment][new_index_in_chunk] = component + new_chunk.__entities[new_index_in_chunk] = entity + new_chunk.__components[fragment][new_index_in_chunk] = component if old_chunk ~= nil then - for old_f, old_cs in pairs(old_chunk.components) do - local new_cs = new_chunk.components[old_f] + for old_f, old_cs in pairs(old_chunk.__components) do + local new_cs = new_chunk.__components[old_f] new_cs[new_index_in_chunk] = old_cs[old_index_in_chunk] end end @@ -384,29 +384,29 @@ function registry.insert(entity, fragment, component) __detach_entity(entity) end - entity.chunk = new_chunk - entity.index_in_chunk = new_index_in_chunk + entity.__chunk = new_chunk + entity.__index_in_chunk = new_index_in_chunk end ---@param entity evolved.entity ---@param fragment evolved.entity function registry.remove(entity, fragment) - local old_chunk = entity.chunk + local old_chunk = entity.__chunk local new_chunk = __chunk_without_fragment(old_chunk, fragment) if old_chunk == new_chunk then error(string.format('entity %s does not have fragment %s', entity, fragment), 2) end - local old_index_in_chunk = entity.index_in_chunk - local new_index_in_chunk = new_chunk and #new_chunk.entities + 1 or 0 + local old_index_in_chunk = entity.__index_in_chunk + local new_index_in_chunk = new_chunk and #new_chunk.__entities + 1 or 0 if new_chunk ~= nil then - new_chunk.entities[new_index_in_chunk] = entity + new_chunk.__entities[new_index_in_chunk] = entity if old_chunk ~= nil then - for new_f, new_cs in pairs(new_chunk.components) do - local old_cs = old_chunk.components[new_f] + for new_f, new_cs in pairs(new_chunk.__components) do + local old_cs = old_chunk.__components[new_f] new_cs[new_index_in_chunk] = old_cs[old_index_in_chunk] end end @@ -416,8 +416,8 @@ function registry.remove(entity, fragment) __detach_entity(entity) end - entity.chunk = new_chunk - entity.index_in_chunk = new_index_in_chunk + entity.__chunk = new_chunk + entity.__index_in_chunk = new_index_in_chunk end ---@param fragment evolved.entity @@ -428,12 +428,12 @@ function registry.query(fragment, ...) local fragments = { fragment, ... } table.sort(fragments, function(a, b) - return a.guid < b.guid + return a.__guid < b.__guid end) ---@type evolved.query local query = { - fragments = fragments, + __fragments = fragments, } setmetatable(query, evolved_query_mt) @@ -446,14 +446,14 @@ end ---@return fun(): evolved.chunk? ---@nodiscard function registry.execute(query) - local main_fragment = query.fragments[#query.fragments] + local main_fragment = query.__fragments[#query.__fragments] local main_fragment_chunks = __chunks[main_fragment] or {} ---@type evolved.chunk[] local matched_chunk_stack = {} for _, main_fragment_chunk in ipairs(main_fragment_chunks) do - if __chunk_has_all_fragments(main_fragment_chunk, compat.unpack(query.fragments)) then + if __chunk_has_all_fragments(main_fragment_chunk, compat.unpack(query.__fragments)) then matched_chunk_stack[#matched_chunk_stack + 1] = main_fragment_chunk end end @@ -463,7 +463,7 @@ function registry.execute(query) local matched_chunk = matched_chunk_stack[#matched_chunk_stack] matched_chunk_stack[#matched_chunk_stack] = nil - for _, matched_chunk_child in pairs(matched_chunk.children) do + for _, matched_chunk_child in pairs(matched_chunk.__children) do matched_chunk_stack[#matched_chunk_stack + 1] = matched_chunk_child end @@ -480,7 +480,7 @@ function registry.chunk(fragment, ...) local fragments = { fragment, ... } table.sort(fragments, function(a, b) - return a.guid < b.guid + return a.__guid < b.__guid end) local chunk = __root_chunk(fragments[1]) @@ -492,6 +492,27 @@ function registry.chunk(fragment, ...) return chunk end +---@param chunk evolved.chunk +---@return evolved.entity[] +---@nodiscard +function registry.chunk_entities(chunk) + return chunk.__entities +end + +---@param chunk evolved.chunk +---@param fragment evolved.entity +---@return any[] +---@nodiscard +function registry.chunk_components(chunk, fragment) + local components = chunk.__components[fragment] + + if components == nil then + error(string.format('chunk %s does not have fragment %s', chunk, fragment), 2) + end + + return components +end + --- --- --- @@ -499,7 +520,7 @@ end --- function evolved_entity_mt:__tostring() - local index, version = idpools.unpack_id(self.guid) + local index, version = idpools.unpack_id(self.__guid) return string.format('[%d;%d]', index, version) end @@ -507,7 +528,7 @@ end function evolved_query_mt:__tostring() local fragment_ids = '' - for _, fragment in ipairs(self.fragments) do + for _, fragment in ipairs(self.__fragments) do fragment_ids = string.format('%s%s', fragment_ids, fragment) end @@ -518,8 +539,8 @@ function evolved_chunk_mt:__tostring() local fragment_ids = '' local chunk_iter = self; while chunk_iter do - fragment_ids = string.format('%s%s', chunk_iter.fragment, fragment_ids) - chunk_iter = chunk_iter.parent + fragment_ids = string.format('%s%s', chunk_iter.__fragment, fragment_ids) + chunk_iter = chunk_iter.__parent end return string.format('{%s}', fragment_ids)