mirror of
https://github.com/BlackMATov/evolved.lua.git
synced 2025-12-15 12:19:47 +07:00
first chunk tree impl
This commit is contained in:
9
.vscode/launch.json
vendored
9
.vscode/launch.json
vendored
@@ -1,6 +1,15 @@
|
|||||||
{
|
{
|
||||||
"version": "0.2.0",
|
"version": "0.2.0",
|
||||||
"configurations": [
|
"configurations": [
|
||||||
|
{
|
||||||
|
"name": "Launch Evolved All",
|
||||||
|
"type": "lua-local",
|
||||||
|
"request": "launch",
|
||||||
|
"program": {
|
||||||
|
"lua": "luajit",
|
||||||
|
"file": "${workspaceFolder}/develop/all.lua"
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "Launch Evolved Example",
|
"name": "Launch Evolved Example",
|
||||||
"type": "lua-local",
|
"type": "lua-local",
|
||||||
|
|||||||
3
develop/all.lua
Normal file
3
develop/all.lua
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
require 'develop.example'
|
||||||
|
require 'develop.unbench'
|
||||||
|
require 'develop.untests'
|
||||||
@@ -1 +1,14 @@
|
|||||||
local evolved = require 'evolved'
|
local evolved = require 'evolved'
|
||||||
|
|
||||||
|
local registry = evolved.registry()
|
||||||
|
|
||||||
|
local fragments = {
|
||||||
|
position = registry:entity(),
|
||||||
|
velocity = registry:entity(),
|
||||||
|
}
|
||||||
|
|
||||||
|
do
|
||||||
|
local entity = registry:entity()
|
||||||
|
entity:insert(fragments.position)
|
||||||
|
entity:insert(fragments.velocity)
|
||||||
|
end
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
local evolved = require 'evolved'
|
local evolved = require 'evolved'
|
||||||
|
local utilities = require 'develop.utilities'
|
||||||
|
|
||||||
---@param name string
|
---@param name string
|
||||||
---@param func fun(...):...
|
---@param func fun(...):...
|
||||||
@@ -6,7 +7,7 @@ local evolved = require 'evolved'
|
|||||||
local function describe(name, func, ...)
|
local function describe(name, func, ...)
|
||||||
collectgarbage('stop')
|
collectgarbage('stop')
|
||||||
|
|
||||||
print(string.format('- %s ...', name))
|
print(string.format('| unbench | %s ...', name))
|
||||||
|
|
||||||
local start_s = os.clock()
|
local start_s = os.clock()
|
||||||
local start_kb = collectgarbage('count')
|
local start_kb = collectgarbage('count')
|
||||||
@@ -23,3 +24,18 @@ local function describe(name, func, ...)
|
|||||||
|
|
||||||
collectgarbage('restart')
|
collectgarbage('restart')
|
||||||
end
|
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,4 +1,5 @@
|
|||||||
local evolved = require 'evolved'
|
local evolved = require 'evolved'
|
||||||
|
local utilities = require 'develop.utilities'
|
||||||
|
|
||||||
---@param name string
|
---@param name string
|
||||||
---@param func fun(...):...
|
---@param func fun(...):...
|
||||||
@@ -6,7 +7,7 @@ local evolved = require 'evolved'
|
|||||||
local function describe(name, func, ...)
|
local function describe(name, func, ...)
|
||||||
collectgarbage('stop')
|
collectgarbage('stop')
|
||||||
|
|
||||||
print(string.format('- %s ...', name))
|
print(string.format('| untests | %s ...', name))
|
||||||
|
|
||||||
local start_s = os.clock()
|
local start_s = os.clock()
|
||||||
local start_kb = collectgarbage('count')
|
local start_kb = collectgarbage('count')
|
||||||
@@ -23,3 +24,72 @@ local function describe(name, func, ...)
|
|||||||
|
|
||||||
collectgarbage('restart')
|
collectgarbage('restart')
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe('random entity:insert', function()
|
||||||
|
for _ = 1, 1000 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
|
||||||
|
|
||||||
|
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)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe('random entity:remove', function()
|
||||||
|
for _ = 1, 1000 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)
|
||||||
|
|||||||
11
develop/utilities.lua
Normal file
11
develop/utilities.lua
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
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
|
||||||
234
evolved.lua
234
evolved.lua
@@ -1,4 +1,238 @@
|
|||||||
---@class evolved
|
---@class evolved
|
||||||
local evolved = {}
|
local evolved = {}
|
||||||
|
|
||||||
|
---
|
||||||
|
---
|
||||||
|
---
|
||||||
|
---
|
||||||
|
---
|
||||||
|
|
||||||
|
---@class evolved.chunk
|
||||||
|
---@field owner evolved.registry
|
||||||
|
---@field parent? evolved.chunk
|
||||||
|
---@field fragment? evolved.entity
|
||||||
|
---@field entities evolved.entity[]
|
||||||
|
---@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.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[]
|
||||||
|
local evolved_registry_mt = {}
|
||||||
|
evolved_registry_mt.__index = evolved_registry_mt
|
||||||
|
|
||||||
|
---
|
||||||
|
---
|
||||||
|
---
|
||||||
|
---
|
||||||
|
---
|
||||||
|
|
||||||
|
---@param owner evolved.registry
|
||||||
|
---@param parent? evolved.chunk
|
||||||
|
---@param fragment? evolved.entity
|
||||||
|
---@return evolved.chunk
|
||||||
|
local function create_chunk(owner, parent, fragment)
|
||||||
|
---@type evolved.chunk
|
||||||
|
local chunk = {
|
||||||
|
owner = owner,
|
||||||
|
parent = parent,
|
||||||
|
fragment = fragment,
|
||||||
|
entities = {},
|
||||||
|
with_cache = {},
|
||||||
|
without_cache = {},
|
||||||
|
}
|
||||||
|
return setmetatable(chunk, evolved_chunk_mt)
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param owner evolved.registry
|
||||||
|
---@param id integer
|
||||||
|
local function create_entity(owner, id)
|
||||||
|
---@type evolved.entity
|
||||||
|
local entity = {
|
||||||
|
owner = owner,
|
||||||
|
id = id,
|
||||||
|
}
|
||||||
|
return setmetatable(entity, evolved_entity_mt)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function create_registry()
|
||||||
|
---@type evolved.registry
|
||||||
|
local registry = {
|
||||||
|
nextid = 1,
|
||||||
|
chunks = {},
|
||||||
|
}
|
||||||
|
|
||||||
|
registry.chunks[1] = create_chunk(registry)
|
||||||
|
|
||||||
|
return setmetatable(registry, evolved_registry_mt)
|
||||||
|
end
|
||||||
|
|
||||||
|
---
|
||||||
|
---
|
||||||
|
--- 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 not self.fragment or self.fragment.id < fragment.id then
|
||||||
|
local new_chunk = create_chunk(self.owner, self, fragment)
|
||||||
|
self.owner.chunks[#self.owner.chunks + 1] = new_chunk
|
||||||
|
|
||||||
|
self.with_cache[fragment] = new_chunk
|
||||||
|
new_chunk.without_cache[fragment] = self
|
||||||
|
|
||||||
|
return new_chunk
|
||||||
|
end
|
||||||
|
|
||||||
|
do
|
||||||
|
local sibling_chunk = self.parent
|
||||||
|
:with(fragment)
|
||||||
|
:with(self.fragment)
|
||||||
|
|
||||||
|
self.with_cache[fragment] = sibling_chunk
|
||||||
|
|
||||||
|
return sibling_chunk
|
||||||
|
end
|
||||||
|
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
|
||||||
|
|
||||||
|
do
|
||||||
|
local sibling_chunk = self.parent
|
||||||
|
:without(fragment)
|
||||||
|
:with(self.fragment)
|
||||||
|
|
||||||
|
self.without_cache[fragment] = sibling_chunk
|
||||||
|
|
||||||
|
return sibling_chunk
|
||||||
|
end
|
||||||
|
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
|
||||||
|
|
||||||
|
---
|
||||||
|
---
|
||||||
|
--- 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
|
||||||
|
---
|
||||||
|
---
|
||||||
|
|
||||||
|
---@return evolved.chunk
|
||||||
|
function evolved_registry_mt:root()
|
||||||
|
return self.chunks[1]
|
||||||
|
end
|
||||||
|
|
||||||
|
---@return evolved.entity
|
||||||
|
function evolved_registry_mt:entity()
|
||||||
|
local id = self.nextid
|
||||||
|
self.nextid = self.nextid + 1
|
||||||
|
local entity = create_entity(self, id)
|
||||||
|
self.chunks[1]:insert(entity)
|
||||||
|
return entity
|
||||||
|
end
|
||||||
|
|
||||||
|
---
|
||||||
|
---
|
||||||
|
--- MODULE API
|
||||||
|
---
|
||||||
|
---
|
||||||
|
|
||||||
|
---@return evolved.registry
|
||||||
|
function evolved.registry()
|
||||||
|
return create_registry()
|
||||||
|
end
|
||||||
|
|
||||||
|
---
|
||||||
|
---
|
||||||
|
---
|
||||||
|
---
|
||||||
|
---
|
||||||
|
|
||||||
return evolved
|
return evolved
|
||||||
|
|||||||
Reference in New Issue
Block a user