mirror of
https://github.com/BlackMATov/evolved.lua.git
synced 2026-01-04 17:20:58 +07:00
from scratch
This commit is contained in:
2
.vscode/extensions.json
vendored
2
.vscode/extensions.json
vendored
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"recommendations": [
|
"recommendations": [
|
||||||
"sumneko.lua",
|
"sumneko.lua"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
2
.vscode/settings.json
vendored
2
.vscode/settings.json
vendored
@@ -3,6 +3,6 @@
|
|||||||
"editor.formatOnSave": true,
|
"editor.formatOnSave": true,
|
||||||
"files.insertFinalNewline": true,
|
"files.insertFinalNewline": true,
|
||||||
"files.trimFinalNewlines": true,
|
"files.trimFinalNewlines": true,
|
||||||
"files.trimTrailingWhitespace": true,
|
"files.trimTrailingWhitespace": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,34 +1 @@
|
|||||||
local evolved = require 'evolved'
|
local evolved = require 'evolved.evolved'
|
||||||
|
|
||||||
local registry = evolved.registry()
|
|
||||||
|
|
||||||
local fragments = {
|
|
||||||
position = registry:entity(),
|
|
||||||
velocity = registry:entity(),
|
|
||||||
}
|
|
||||||
|
|
||||||
do
|
|
||||||
registry:entity(
|
|
||||||
fragments.position,
|
|
||||||
fragments.velocity)
|
|
||||||
end
|
|
||||||
|
|
||||||
do
|
|
||||||
local entity = registry:entity()
|
|
||||||
entity:insert(fragments.position)
|
|
||||||
entity:insert(fragments.velocity)
|
|
||||||
end
|
|
||||||
|
|
||||||
do
|
|
||||||
local query = registry:query(
|
|
||||||
fragments.position,
|
|
||||||
fragments.velocity)
|
|
||||||
|
|
||||||
for chunk in query:chunks() do
|
|
||||||
local ps = chunk.components[fragments.position]
|
|
||||||
local vs = chunk.components[fragments.position]
|
|
||||||
|
|
||||||
for i = 1, #chunk.entities do
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|||||||
@@ -1,78 +0,0 @@
|
|||||||
local iterators = {}
|
|
||||||
|
|
||||||
---@generic K
|
|
||||||
---@param t table<K, any>
|
|
||||||
---@return fun(): K
|
|
||||||
function iterators.keys(t)
|
|
||||||
if #t > 0 then
|
|
||||||
local i = 0
|
|
||||||
return function()
|
|
||||||
i = i + 1
|
|
||||||
if i <= #t then
|
|
||||||
return i
|
|
||||||
end
|
|
||||||
end
|
|
||||||
else
|
|
||||||
local k = nil
|
|
||||||
return function()
|
|
||||||
local v
|
|
||||||
k, v = next(t, k)
|
|
||||||
if k ~= nil then
|
|
||||||
return k
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
---@generic V
|
|
||||||
---@param t table<any, V>
|
|
||||||
---@return fun(): V?
|
|
||||||
function iterators.values(t)
|
|
||||||
if #t > 0 then
|
|
||||||
local i = 0
|
|
||||||
return function()
|
|
||||||
i = i + 1
|
|
||||||
if i <= #t then
|
|
||||||
return t[i]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
else
|
|
||||||
local k = nil
|
|
||||||
return function()
|
|
||||||
local v
|
|
||||||
k, v = next(t, k)
|
|
||||||
if k ~= nil then
|
|
||||||
return v
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
---@generic V
|
|
||||||
---@param iter fun(): V?
|
|
||||||
---@return integer
|
|
||||||
function iterators.count(iter)
|
|
||||||
local count = 0
|
|
||||||
for _ in iter do count = count + 1 end
|
|
||||||
return count
|
|
||||||
end
|
|
||||||
|
|
||||||
---@generic V
|
|
||||||
---@param iter fun(): V?
|
|
||||||
---@param func fun(v: V): boolean
|
|
||||||
---@return fun(): V?
|
|
||||||
function iterators.filter(iter, func)
|
|
||||||
return function()
|
|
||||||
while true do
|
|
||||||
local v = iter()
|
|
||||||
if v == nil then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
if func(v) then
|
|
||||||
return v
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return iterators
|
|
||||||
@@ -1,41 +1 @@
|
|||||||
local evolved = require 'evolved'
|
local evolved = require 'evolved.evolved'
|
||||||
local utilities = require 'develop.utilities'
|
|
||||||
|
|
||||||
---@param name string
|
|
||||||
---@param func fun(...):...
|
|
||||||
---@param ... any
|
|
||||||
local function describe(name, func, ...)
|
|
||||||
collectgarbage('stop')
|
|
||||||
|
|
||||||
print(string.format('| unbench | %s ...', name))
|
|
||||||
|
|
||||||
local start_s = os.clock()
|
|
||||||
local start_kb = collectgarbage('count')
|
|
||||||
|
|
||||||
local success, result = pcall(func, ...)
|
|
||||||
|
|
||||||
local finish_s = os.clock() - start_s
|
|
||||||
local finish_kb = collectgarbage('count') - start_kb
|
|
||||||
|
|
||||||
print(string.format(' %s | ms: %.2f | mb: %.2f',
|
|
||||||
success and 'OK' or 'FAILED', finish_s * 1000, finish_kb / 1024))
|
|
||||||
|
|
||||||
if not success then print(' ' .. result) end
|
|
||||||
|
|
||||||
collectgarbage('restart')
|
|
||||||
end
|
|
||||||
|
|
||||||
describe('memory footprint of 1k entities', function()
|
|
||||||
local registry = evolved.registry()
|
|
||||||
for _ = 1, 1000 do registry:entity() end
|
|
||||||
end)
|
|
||||||
|
|
||||||
describe('memory footprint of 10k entities', function()
|
|
||||||
local registry = evolved.registry()
|
|
||||||
for _ = 1, 10000 do registry:entity() end
|
|
||||||
end)
|
|
||||||
|
|
||||||
describe('memory footprint of 100k entities', function()
|
|
||||||
local registry = evolved.registry()
|
|
||||||
for _ = 1, 100000 do registry:entity() end
|
|
||||||
end)
|
|
||||||
|
|||||||
@@ -1,152 +1 @@
|
|||||||
local evolved = require 'evolved'
|
local evolved = require 'evolved.evolved'
|
||||||
|
|
||||||
local iterators = require 'develop.iterators'
|
|
||||||
local utilities = require 'develop.utilities'
|
|
||||||
|
|
||||||
---@param name string
|
|
||||||
---@param func fun(...):...
|
|
||||||
---@param ... any
|
|
||||||
local function describe(name, func, ...)
|
|
||||||
collectgarbage('stop')
|
|
||||||
|
|
||||||
print(string.format('| untests | %s ...', name))
|
|
||||||
|
|
||||||
local start_s = os.clock()
|
|
||||||
local start_kb = collectgarbage('count')
|
|
||||||
|
|
||||||
local success, result = pcall(func, ...)
|
|
||||||
|
|
||||||
local finish_s = os.clock() - start_s
|
|
||||||
local finish_kb = collectgarbage('count') - start_kb
|
|
||||||
|
|
||||||
print(string.format(' %s | ms: %.2f | mb: %.2f',
|
|
||||||
success and 'OK' or 'FAILED', finish_s * 1000, finish_kb / 1024))
|
|
||||||
|
|
||||||
if not success then print(' ' .. result) end
|
|
||||||
|
|
||||||
collectgarbage('restart')
|
|
||||||
end
|
|
||||||
|
|
||||||
describe('entity:entity', function()
|
|
||||||
for _ = 1, 500 do
|
|
||||||
local registry = evolved.registry()
|
|
||||||
|
|
||||||
---@type evolved.entity[]
|
|
||||||
local all_fragments = {}
|
|
||||||
local all_fragment_count = math.random(1, 10)
|
|
||||||
for i = 1, all_fragment_count do all_fragments[i] = registry:entity() end
|
|
||||||
|
|
||||||
for _ = 1, 100 do
|
|
||||||
---@type evolved.entity[]
|
|
||||||
local insert_fragments = {}
|
|
||||||
local insert_fragment_count = math.random(1, 10)
|
|
||||||
for i = 1, insert_fragment_count do insert_fragments[i] = all_fragments[math.random(1, all_fragment_count)] end
|
|
||||||
|
|
||||||
local e1 = registry:entity()
|
|
||||||
for _, fragment in ipairs(insert_fragments) do e1:insert(fragment) end
|
|
||||||
|
|
||||||
local e2 = registry:entity(unpack(insert_fragments))
|
|
||||||
|
|
||||||
assert(e1.chunk == e2.chunk)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
|
|
||||||
describe('entity:chunk', function()
|
|
||||||
for _ = 1, 500 do
|
|
||||||
local registry = evolved.registry()
|
|
||||||
|
|
||||||
---@type evolved.entity[]
|
|
||||||
local all_fragments = {}
|
|
||||||
local all_fragment_count = math.random(1, 10)
|
|
||||||
for i = 1, all_fragment_count do all_fragments[i] = registry:entity() end
|
|
||||||
|
|
||||||
for _ = 1, 100 do
|
|
||||||
local e1, e2 = registry:entity(), registry:entity()
|
|
||||||
|
|
||||||
---@type evolved.entity[]
|
|
||||||
local insert_fragments = {}
|
|
||||||
local insert_fragment_count = math.random(1, 10)
|
|
||||||
for i = 1, insert_fragment_count do insert_fragments[i] = all_fragments[math.random(1, all_fragment_count)] end
|
|
||||||
|
|
||||||
---@type evolved.entity[]
|
|
||||||
local remove_fragments = {}
|
|
||||||
local remove_fragment_count = math.random(1, 10)
|
|
||||||
for i = 1, remove_fragment_count do remove_fragments[i] = all_fragments[math.random(1, all_fragment_count)] end
|
|
||||||
|
|
||||||
utilities.shuffle_array(insert_fragments)
|
|
||||||
for _, fragment in ipairs(insert_fragments) do e1:insert(fragment) end
|
|
||||||
|
|
||||||
utilities.shuffle_array(insert_fragments)
|
|
||||||
for _, fragment in ipairs(insert_fragments) do e2:insert(fragment) end
|
|
||||||
|
|
||||||
assert(e1.chunk == e2.chunk)
|
|
||||||
|
|
||||||
utilities.shuffle_array(remove_fragments)
|
|
||||||
for _, fragment in ipairs(remove_fragments) do e1:remove(fragment) end
|
|
||||||
|
|
||||||
utilities.shuffle_array(remove_fragments)
|
|
||||||
for _, fragment in ipairs(remove_fragments) do e2:remove(fragment) end
|
|
||||||
|
|
||||||
assert(e1.chunk == e2.chunk)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
|
|
||||||
describe('query:chunks', function()
|
|
||||||
for _ = 1, 500 do
|
|
||||||
local registry = evolved.registry()
|
|
||||||
|
|
||||||
---@type evolved.entity[]
|
|
||||||
local all_fragments = {}
|
|
||||||
local all_fragment_count = math.random(1, 10)
|
|
||||||
for i = 1, all_fragment_count do all_fragments[i] = registry:entity() end
|
|
||||||
|
|
||||||
local function check_all_queries()
|
|
||||||
for _, q in ipairs(registry.queries) do
|
|
||||||
local query_chunk_count = iterators.count(q:chunks())
|
|
||||||
local registry_chunk_count = iterators.count(iterators.filter(
|
|
||||||
iterators.values(registry.chunks),
|
|
||||||
function(chunk) return chunk:has_all_fragments(unpack(q.fragments)) end))
|
|
||||||
assert(query_chunk_count == registry_chunk_count)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
---@param n integer
|
|
||||||
local function create_queries(n)
|
|
||||||
for _ = 1, n do
|
|
||||||
---@type evolved.entity[]
|
|
||||||
local query_fragments = {}
|
|
||||||
local query_fragment_count = math.random(1, 5)
|
|
||||||
for i = 1, query_fragment_count do
|
|
||||||
query_fragments[i] = all_fragments[math.random(1, all_fragment_count)]
|
|
||||||
end
|
|
||||||
|
|
||||||
registry:query(unpack(query_fragments))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
---@param n integer
|
|
||||||
local function create_entities(n)
|
|
||||||
for _ = 1, n do
|
|
||||||
---@type evolved.entity[]
|
|
||||||
local insert_fragments = {}
|
|
||||||
local insert_fragment_count = math.random(1, 10)
|
|
||||||
for i = 1, insert_fragment_count do
|
|
||||||
insert_fragments[i] = all_fragments[math.random(1, all_fragment_count)]
|
|
||||||
end
|
|
||||||
|
|
||||||
local e = registry:entity()
|
|
||||||
for _, fragment in ipairs(insert_fragments) do e:insert(fragment) end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
create_entities(50)
|
|
||||||
create_queries(10)
|
|
||||||
check_all_queries()
|
|
||||||
|
|
||||||
create_entities(50)
|
|
||||||
create_queries(10)
|
|
||||||
check_all_queries()
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
|
|||||||
@@ -1,11 +0,0 @@
|
|||||||
local utilities = {}
|
|
||||||
|
|
||||||
---@param vs any[]
|
|
||||||
function utilities.shuffle_array(vs)
|
|
||||||
for i = 1, #vs do
|
|
||||||
local j = math.random(i, #vs)
|
|
||||||
vs[i], vs[j] = vs[j], vs[i]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return utilities
|
|
||||||
397
evolved.lua
397
evolved.lua
@@ -1,397 +0,0 @@
|
|||||||
---@class evolved
|
|
||||||
local evolved = {}
|
|
||||||
|
|
||||||
---
|
|
||||||
---
|
|
||||||
---
|
|
||||||
---
|
|
||||||
---
|
|
||||||
|
|
||||||
---@class evolved.chunk
|
|
||||||
---@field owner evolved.registry
|
|
||||||
---@field parent? evolved.chunk
|
|
||||||
---@field fragment? evolved.entity
|
|
||||||
---@field children evolved.chunk[]
|
|
||||||
---@field entities evolved.entity[]
|
|
||||||
---@field components table<evolved.entity, any[]>
|
|
||||||
---@field with_cache table<evolved.entity, evolved.chunk>
|
|
||||||
---@field without_cache table<evolved.entity, evolved.chunk>
|
|
||||||
local evolved_chunk_mt = {}
|
|
||||||
evolved_chunk_mt.__index = evolved_chunk_mt
|
|
||||||
|
|
||||||
---@class evolved.query
|
|
||||||
---@field owner evolved.registry
|
|
||||||
---@field id integer
|
|
||||||
---@field roots evolved.chunk[]
|
|
||||||
---@field fragments evolved.entity[]
|
|
||||||
local evolved_query_mt = {}
|
|
||||||
evolved_query_mt.__index = evolved_query_mt
|
|
||||||
|
|
||||||
---@class evolved.entity
|
|
||||||
---@field owner evolved.registry
|
|
||||||
---@field id integer
|
|
||||||
---@field chunk? evolved.chunk
|
|
||||||
---@field index_in_chunk? integer
|
|
||||||
local evolved_entity_mt = {}
|
|
||||||
evolved_entity_mt.__index = evolved_entity_mt
|
|
||||||
|
|
||||||
---@class evolved.registry
|
|
||||||
---@field nextid integer
|
|
||||||
---@field chunks evolved.chunk[]
|
|
||||||
---@field queries evolved.query[]
|
|
||||||
---@field entities evolved.entity[]
|
|
||||||
---@field chunks_by_fragment table<evolved.entity, evolved.chunk[]>
|
|
||||||
---@field queries_by_fragment table<evolved.entity, evolved.query[]>
|
|
||||||
local evolved_registry_mt = {}
|
|
||||||
evolved_registry_mt.__index = evolved_registry_mt
|
|
||||||
|
|
||||||
---
|
|
||||||
---
|
|
||||||
--- CHUNK API
|
|
||||||
---
|
|
||||||
---
|
|
||||||
|
|
||||||
function evolved_chunk_mt:__tostring()
|
|
||||||
local id, iter = '', self
|
|
||||||
while iter and iter.parent and iter.fragment do
|
|
||||||
id, iter = iter.fragment.id .. (id == '' and '' or ',') .. id, iter.parent
|
|
||||||
end
|
|
||||||
return string.format('evolved.chunk(%s)', id)
|
|
||||||
end
|
|
||||||
|
|
||||||
---@param fragment evolved.entity
|
|
||||||
---@return evolved.chunk
|
|
||||||
function evolved_chunk_mt:with(fragment)
|
|
||||||
do
|
|
||||||
local with_chunk = self.with_cache[fragment]
|
|
||||||
if with_chunk then return with_chunk end
|
|
||||||
end
|
|
||||||
|
|
||||||
if self.fragment and self.fragment.id == fragment.id then
|
|
||||||
return self
|
|
||||||
end
|
|
||||||
|
|
||||||
if self.fragment and self.fragment.id > fragment.id then
|
|
||||||
local sibling_chunk = self.parent
|
|
||||||
:with(fragment)
|
|
||||||
:with(self.fragment)
|
|
||||||
|
|
||||||
self.with_cache[fragment] = sibling_chunk
|
|
||||||
|
|
||||||
return sibling_chunk
|
|
||||||
end
|
|
||||||
|
|
||||||
---@type evolved.chunk
|
|
||||||
local new_chunk = {
|
|
||||||
owner = self.owner,
|
|
||||||
parent = self,
|
|
||||||
fragment = fragment,
|
|
||||||
children = {},
|
|
||||||
entities = {},
|
|
||||||
components = {},
|
|
||||||
with_cache = {},
|
|
||||||
without_cache = {},
|
|
||||||
}
|
|
||||||
setmetatable(new_chunk, evolved_chunk_mt)
|
|
||||||
self.owner.chunks[#self.owner.chunks + 1] = new_chunk
|
|
||||||
|
|
||||||
do
|
|
||||||
local iter = new_chunk
|
|
||||||
while iter and iter.fragment do
|
|
||||||
new_chunk.components[iter.fragment] = {}
|
|
||||||
iter = iter.parent
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
do
|
|
||||||
self.children[#self.children + 1] = new_chunk
|
|
||||||
end
|
|
||||||
|
|
||||||
do
|
|
||||||
local chunks = self.owner.chunks_by_fragment[fragment] or {}
|
|
||||||
chunks[#chunks + 1] = new_chunk
|
|
||||||
self.owner.chunks_by_fragment[fragment] = chunks
|
|
||||||
end
|
|
||||||
|
|
||||||
do
|
|
||||||
local queries = self.owner.queries_by_fragment[fragment] or {}
|
|
||||||
for _, query in ipairs(queries) do
|
|
||||||
if new_chunk:has_all_fragments(unpack(query.fragments)) then
|
|
||||||
query.roots[#query.roots + 1] = new_chunk
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
self.with_cache[fragment] = new_chunk
|
|
||||||
new_chunk.without_cache[fragment] = self
|
|
||||||
|
|
||||||
return new_chunk
|
|
||||||
end
|
|
||||||
|
|
||||||
---@param fragment evolved.entity
|
|
||||||
---@return evolved.chunk
|
|
||||||
function evolved_chunk_mt:without(fragment)
|
|
||||||
do
|
|
||||||
local without_chunk = self.without_cache[fragment]
|
|
||||||
if without_chunk then return without_chunk end
|
|
||||||
end
|
|
||||||
|
|
||||||
if not self.fragment or self.fragment.id < fragment.id then
|
|
||||||
return self
|
|
||||||
end
|
|
||||||
|
|
||||||
local sibling_chunk = self.parent
|
|
||||||
:without(fragment)
|
|
||||||
:with(self.fragment)
|
|
||||||
|
|
||||||
self.without_cache[fragment] = sibling_chunk
|
|
||||||
|
|
||||||
return sibling_chunk
|
|
||||||
end
|
|
||||||
|
|
||||||
---@param entity evolved.entity
|
|
||||||
function evolved_chunk_mt:insert(entity)
|
|
||||||
self.entities[#self.entities + 1] = entity
|
|
||||||
entity.chunk, entity.index_in_chunk = self, #self.entities
|
|
||||||
end
|
|
||||||
|
|
||||||
---@param entity evolved.entity
|
|
||||||
function evolved_chunk_mt:remove(entity)
|
|
||||||
local last_entity = self.entities[#self.entities]
|
|
||||||
|
|
||||||
if entity ~= last_entity then
|
|
||||||
self.entities[entity.index_in_chunk] = last_entity
|
|
||||||
last_entity.index_in_chunk = entity.index_in_chunk
|
|
||||||
end
|
|
||||||
|
|
||||||
self.entities[#self.entities] = nil
|
|
||||||
entity.chunk, entity.index_in_chunk = nil, 0
|
|
||||||
end
|
|
||||||
|
|
||||||
---@param fragment evolved.entity
|
|
||||||
---@return boolean
|
|
||||||
---@nodiscard
|
|
||||||
function evolved_chunk_mt:has_fragment(fragment)
|
|
||||||
return self.components[fragment] ~= nil
|
|
||||||
end
|
|
||||||
|
|
||||||
---@param ... evolved.entity
|
|
||||||
---@return boolean
|
|
||||||
---@nodiscard
|
|
||||||
function evolved_chunk_mt:has_all_fragments(...)
|
|
||||||
local fragment_count = select('#', ...)
|
|
||||||
|
|
||||||
for i = 1, fragment_count do
|
|
||||||
local fragment = select(i, ...)
|
|
||||||
if self.components[fragment] == nil then
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
---@param ... evolved.entity
|
|
||||||
---@return boolean
|
|
||||||
---@nodiscard
|
|
||||||
function evolved_chunk_mt:has_any_fragment(...)
|
|
||||||
local fragment_count = select('#', ...)
|
|
||||||
|
|
||||||
for i = 1, fragment_count do
|
|
||||||
local fragment = select(i, ...)
|
|
||||||
if self.components[fragment] ~= nil then
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
---
|
|
||||||
---
|
|
||||||
--- QUERY API
|
|
||||||
---
|
|
||||||
---
|
|
||||||
|
|
||||||
function evolved_query_mt:__tostring()
|
|
||||||
return string.format('evolved.query(%d)', self.id)
|
|
||||||
end
|
|
||||||
|
|
||||||
---@return fun(): evolved.chunk?
|
|
||||||
function evolved_query_mt:chunks()
|
|
||||||
return coroutine.wrap(function()
|
|
||||||
local queue = {}
|
|
||||||
|
|
||||||
for i = #self.roots, 1, -1 do
|
|
||||||
queue[#queue + 1] = self.roots[i]
|
|
||||||
end
|
|
||||||
|
|
||||||
while #queue > 0 do
|
|
||||||
local chunk = table.remove(queue)
|
|
||||||
|
|
||||||
coroutine.yield(chunk)
|
|
||||||
|
|
||||||
for i = #chunk.children, 1, -1 do
|
|
||||||
queue[#queue + 1] = chunk.children[i]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
|
|
||||||
---
|
|
||||||
---
|
|
||||||
--- ENTITY API
|
|
||||||
---
|
|
||||||
---
|
|
||||||
|
|
||||||
function evolved_entity_mt:__tostring()
|
|
||||||
return string.format('evolved.entity(%d)', self.id)
|
|
||||||
end
|
|
||||||
|
|
||||||
function evolved_entity_mt:destroy()
|
|
||||||
self.chunk:remove(self)
|
|
||||||
end
|
|
||||||
|
|
||||||
---@param fragment evolved.entity
|
|
||||||
function evolved_entity_mt:insert(fragment)
|
|
||||||
local old_chunk = assert(self.chunk)
|
|
||||||
local new_chunk = old_chunk:with(fragment)
|
|
||||||
|
|
||||||
old_chunk:remove(self)
|
|
||||||
new_chunk:insert(self)
|
|
||||||
end
|
|
||||||
|
|
||||||
---@param fragment evolved.entity
|
|
||||||
function evolved_entity_mt:remove(fragment)
|
|
||||||
local old_chunk = assert(self.chunk)
|
|
||||||
local new_chunk = old_chunk:without(fragment)
|
|
||||||
|
|
||||||
old_chunk:remove(self)
|
|
||||||
new_chunk:insert(self)
|
|
||||||
end
|
|
||||||
|
|
||||||
---
|
|
||||||
---
|
|
||||||
--- REGISTRY API
|
|
||||||
---
|
|
||||||
---
|
|
||||||
|
|
||||||
---@param ... evolved.entity
|
|
||||||
---@return evolved.chunk
|
|
||||||
function evolved_registry_mt:chunk(...)
|
|
||||||
local chunk = self.chunks[1]
|
|
||||||
|
|
||||||
for i = 1, select('#', ...) do
|
|
||||||
chunk = chunk:with(select(i, ...))
|
|
||||||
end
|
|
||||||
|
|
||||||
return chunk
|
|
||||||
end
|
|
||||||
|
|
||||||
---@param ... evolved.entity
|
|
||||||
---@return evolved.query
|
|
||||||
function evolved_registry_mt:query(...)
|
|
||||||
local id = self.nextid
|
|
||||||
self.nextid = self.nextid + 1
|
|
||||||
|
|
||||||
---@type evolved.query
|
|
||||||
local query = {
|
|
||||||
owner = self,
|
|
||||||
id = id,
|
|
||||||
roots = {},
|
|
||||||
fragments = {},
|
|
||||||
}
|
|
||||||
setmetatable(query, evolved_query_mt)
|
|
||||||
self.queries[#self.queries + 1] = query
|
|
||||||
|
|
||||||
do
|
|
||||||
local fragments = { ... }
|
|
||||||
table.sort(fragments, function(a, b) return a.id < b.id end)
|
|
||||||
query.fragments = fragments
|
|
||||||
end
|
|
||||||
|
|
||||||
if #query.fragments > 0 then
|
|
||||||
local fragment = query.fragments[#query.fragments]
|
|
||||||
local chunks = self.chunks_by_fragment[fragment] or {}
|
|
||||||
for _, chunk in ipairs(chunks) do
|
|
||||||
if chunk:has_all_fragments(...) then
|
|
||||||
query.roots[#query.roots + 1] = chunk
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if #query.fragments > 0 then
|
|
||||||
local fragment = query.fragments[#query.fragments]
|
|
||||||
local queries = self.queries_by_fragment[fragment] or {}
|
|
||||||
queries[#queries + 1] = query
|
|
||||||
self.queries_by_fragment[fragment] = queries
|
|
||||||
end
|
|
||||||
|
|
||||||
return query
|
|
||||||
end
|
|
||||||
|
|
||||||
---@param ... evolved.entity
|
|
||||||
---@return evolved.entity
|
|
||||||
function evolved_registry_mt:entity(...)
|
|
||||||
local id = self.nextid
|
|
||||||
self.nextid = self.nextid + 1
|
|
||||||
|
|
||||||
---@type evolved.entity
|
|
||||||
local entity = {
|
|
||||||
owner = self,
|
|
||||||
id = id,
|
|
||||||
chunk = nil,
|
|
||||||
index_in_chunk = nil,
|
|
||||||
}
|
|
||||||
setmetatable(entity, evolved_entity_mt)
|
|
||||||
self.entities[#self.entities + 1] = entity
|
|
||||||
|
|
||||||
self:chunk(...):insert(entity)
|
|
||||||
|
|
||||||
return entity
|
|
||||||
end
|
|
||||||
|
|
||||||
---
|
|
||||||
---
|
|
||||||
--- MODULE API
|
|
||||||
---
|
|
||||||
---
|
|
||||||
|
|
||||||
---@return evolved.registry
|
|
||||||
function evolved.registry()
|
|
||||||
---@type evolved.registry
|
|
||||||
local registry = {
|
|
||||||
nextid = 1,
|
|
||||||
chunks = {},
|
|
||||||
queries = {},
|
|
||||||
entities = {},
|
|
||||||
chunks_by_fragment = {},
|
|
||||||
queries_by_fragment = {},
|
|
||||||
}
|
|
||||||
setmetatable(registry, evolved_registry_mt)
|
|
||||||
|
|
||||||
---@type evolved.chunk
|
|
||||||
local root_chunk = {
|
|
||||||
owner = registry,
|
|
||||||
parent = nil,
|
|
||||||
fragment = nil,
|
|
||||||
children = {},
|
|
||||||
entities = {},
|
|
||||||
components = {},
|
|
||||||
with_cache = {},
|
|
||||||
without_cache = {},
|
|
||||||
}
|
|
||||||
setmetatable(root_chunk, evolved_chunk_mt)
|
|
||||||
|
|
||||||
registry.chunks[1] = root_chunk
|
|
||||||
|
|
||||||
return registry
|
|
||||||
end
|
|
||||||
|
|
||||||
---
|
|
||||||
---
|
|
||||||
---
|
|
||||||
---
|
|
||||||
---
|
|
||||||
|
|
||||||
return evolved
|
|
||||||
4
evolved/evolved.lua
Normal file
4
evolved/evolved.lua
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
---@class evolved
|
||||||
|
local evolved = {}
|
||||||
|
|
||||||
|
return evolved
|
||||||
Reference in New Issue
Block a user