From 253d9e2246f2e38559fcf9af2e1892206160c92d Mon Sep 17 00:00:00 2001 From: BlackMATov Date: Sat, 18 Jan 2025 01:31:20 +0700 Subject: [PATCH] deferred spawning support --- README.md | 2 +- develop/unbench.lua | 344 +++++++++++------------------------ develop/untests.lua | 251 +++++++++++++++++++++---- evolved.lua | 433 +++++++++++++++++++++++++++----------------- 4 files changed, 584 insertions(+), 446 deletions(-) diff --git a/README.md b/README.md index 49b8b92..ba66222 100644 --- a/README.md +++ b/README.md @@ -81,8 +81,8 @@ execute :: query -> {execute_state? -> chunk?, entity[]?}, execute_state? ``` ``` -spawn :: fragment[]?, component[]? -> entity, boolean spawn_at :: chunk?, fragment[]?, component[]? -> entity, boolean +spawn_with :: fragment[]?, component[]? -> entity, boolean ``` ``` diff --git a/develop/unbench.lua b/develop/unbench.lua index 3a8707b..89402ed 100644 --- a/develop/unbench.lua +++ b/develop/unbench.lua @@ -669,128 +669,6 @@ basics.describe_bench(string.format('create and destroy %d entities with 5 compo print '----------------------------------------' -basics.describe_bench(string.format('create and destroy %d entities / spawn', N), - ---@param entities evolved.id[] - function(entities) - local spawn = evo.spawn - local destroy = evo.destroy - - local fragments = {} - local components = {} - - for i = 1, N do - entities[i] = spawn(fragments, components) - end - - for i = 1, #entities do - destroy(entities[i]) - end - end, function() - return {} - end) - -basics.describe_bench(string.format('create and destroy %d entities with 1 components / spawn', N), - ---@param entities evolved.id[] - function(entities) - local spawn = evo.spawn - local destroy = evo.destroy - - local fragments = { F1 } - local components = { true } - - for i = 1, N do - entities[i] = spawn(fragments, components) - end - - for i = 1, #entities do - destroy(entities[i]) - end - end, function() - return {} - end) - -basics.describe_bench(string.format('create and destroy %d entities with 2 components / spawn', N), - ---@param entities evolved.id[] - function(entities) - local spawn = evo.spawn - local destroy = evo.destroy - - local fragments = { F1, F2 } - local components = { true, true } - - for i = 1, N do - entities[i] = spawn(fragments, components) - end - - for i = 1, #entities do - destroy(entities[i]) - end - end, function() - return {} - end) - -basics.describe_bench(string.format('create and destroy %d entities with 3 components / spawn', N), - ---@param entities evolved.id[] - function(entities) - local spawn = evo.spawn - local destroy = evo.destroy - - local fragments = { F1, F2, F3 } - local components = { true, true, true } - - for i = 1, N do - entities[i] = spawn(fragments, components) - end - - for i = 1, #entities do - destroy(entities[i]) - end - end, function() - return {} - end) - -basics.describe_bench(string.format('create and destroy %d entities with 4 components / spawn', N), - ---@param entities evolved.id[] - function(entities) - local spawn = evo.spawn - local destroy = evo.destroy - - local fragments = { F1, F2, F3, F4 } - local components = { true, true, true, true } - - for i = 1, N do - entities[i] = spawn(fragments, components) - end - - for i = 1, #entities do - destroy(entities[i]) - end - end, function() - return {} - end) - -basics.describe_bench(string.format('create and destroy %d entities with 5 components / spawn', N), - ---@param entities evolved.id[] - function(entities) - local spawn = evo.spawn - local destroy = evo.destroy - - local fragments = { F1, F2, F3, F4, F5 } - local components = { true, true, true, true, true } - - for i = 1, N do - entities[i] = spawn(fragments, components) - end - - for i = 1, #entities do - destroy(entities[i]) - end - end, function() - return {} - end) - -print '----------------------------------------' - basics.describe_bench(string.format('create and destroy %d entities / spawn_at', N), ---@param entities evolved.id[] function(entities) @@ -925,132 +803,124 @@ basics.describe_bench(string.format('create and destroy %d entities with 5 compo print '----------------------------------------' ---- ---- initial ---- +basics.describe_bench(string.format('create and destroy %d entities / spawn_with', N), + ---@param entities evolved.id[] + function(entities) + local spawn_with = evo.spawn_with + local destroy = evo.destroy ---[[ lua 5.1 -| create and destroy 1k entities ... | - PASS | us: 312.60 | op/s: 3199.00 | kb/i: 0.05 -| create and destroy 1k entities with 1 component ... | - PASS | us: 1570.31 | op/s: 636.82 | kb/i: 0.63 -| create and destroy 1k entities with 2 components ... | - PASS | us: 2780.82 | op/s: 359.61 | kb/i: 0.91 -| create and destroy 1k entities with 3 components ... | - PASS | us: 4060.00 | op/s: 246.31 | kb/i: 1.67 -]] + local fragments = {} + local components = {} ---[[ luajit 2.1 -| create and destroy 1k entities ... | - PASS | us: 12.22 | op/s: 81840.80 | kb/i: 0.00 -| create and destroy 1k entities with 1 component ... | - PASS | us: 56.22 | op/s: 17786.07 | kb/i: 0.02 -| create and destroy 1k entities with 2 components ... | - PASS | us: 412.73 | op/s: 2422.89 | kb/i: 0.11 -| create and destroy 1k entities with 3 components ... | - PASS | us: 611.62 | op/s: 1635.00 | kb/i: 0.17 -]] + for i = 1, N do + entities[i] = spawn_with(fragments, components) + end ---- ---- unpack ids without dedicated functions ---- + for i = 1, #entities do + destroy(entities[i]) + end + end, function() + return {} + end) ---[[ lua 5.1 -| create and destroy 1k entities ... | - PASS | us: 255.40 | op/s: 3915.42 | kb/i: 0.04 -| create and destroy 1k entities with 1 component ... | - PASS | us: 1248.45 | op/s: 801.00 | kb/i: 0.50 -| create and destroy 1k entities with 2 components ... | - PASS | us: 2208.79 | op/s: 452.74 | kb/i: 0.73 -| create and destroy 1k entities with 3 components ... | - PASS | us: 3278.69 | op/s: 305.00 | kb/i: 1.37 -]] +basics.describe_bench(string.format('create and destroy %d entities with 1 components / spawn_with', N), + ---@param entities evolved.id[] + function(entities) + local spawn_with = evo.spawn_with + local destroy = evo.destroy ---[[ luajit 2.1 -| create and destroy 1k entities ... | - PASS | us: 12.12 | op/s: 82482.59 | kb/i: 0.00 -| create and destroy 1k entities with 1 component ... | - PASS | us: 69.05 | op/s: 14482.59 | kb/i: 0.03 -| create and destroy 1k entities with 2 components ... | - PASS | us: 400.40 | op/s: 2497.51 | kb/i: 0.09 -| create and destroy 1k entities with 3 components ... | - PASS | us: 574.71 | op/s: 1740.00 | kb/i: 0.14 -]] + local fragments = { F1 } + local components = { true } ---- ---- hook flags for chunks ---- + for i = 1, N do + entities[i] = spawn_with(fragments, components) + end ---[[ lua 5.1 -| create and destroy 1k entities ... | - PASS | us: 255.40 | op/s: 3915.42 | kb/i: 0.04 -| create and destroy 1k entities with 1 component ... | - PASS | us: 1005.03 | op/s: 995.00 | kb/i: 0.41 -| create and destroy 1k entities with 2 components ... | - PASS | us: 1747.83 | op/s: 572.14 | kb/i: 0.59 -| create and destroy 1k entities with 3 components ... | - PASS | us: 2576.92 | op/s: 388.06 | kb/i: 1.08 -]] + for i = 1, #entities do + destroy(entities[i]) + end + end, function() + return {} + end) ---[[ luajit 2.1 -| create and destroy 1k entities ... | - PASS | us: 12.20 | op/s: 81940.30 | kb/i: 0.00 -| create and destroy 1k entities with 1 component ... | - PASS | us: 53.66 | op/s: 18636.82 | kb/i: 0.02 -| create and destroy 1k entities with 2 components ... | - PASS | us: 357.02 | op/s: 2801.00 | kb/i: 0.09 -| create and destroy 1k entities with 3 components ... | - PASS | us: 533.33 | op/s: 1875.00 | kb/i: 0.15 -]] +basics.describe_bench(string.format('create and destroy %d entities with 2 components / spawn_with', N), + ---@param entities evolved.id[] + function(entities) + local spawn_with = evo.spawn_with + local destroy = evo.destroy ---- ---- construct flags for chunks ---- + local fragments = { F1, F2 } + local components = { true, true } ---[[ lua 5.1 -| create and destroy 1k entities ... | - PASS | us: 253.49 | op/s: 3945.00 | kb/i: 0.04 -| create and destroy 1k entities with 1 component ... | - PASS | us: 913.64 | op/s: 1094.53 | kb/i: 0.37 -| create and destroy 1k entities with 2 components ... | - PASS | us: 1562.50 | op/s: 640.00 | kb/i: 0.53 -| create and destroy 1k entities with 3 components ... | - PASS | us: 2280.90 | op/s: 438.42 | kb/i: 0.97 -]] + for i = 1, N do + entities[i] = spawn_with(fragments, components) + end ---[[ luajit 2.1 -| create and destroy 1k entities ... | - PASS | us: 12.05 | op/s: 82995.02 | kb/i: 0.00 -| create and destroy 1k entities with 1 component ... | - PASS | us: 53.61 | op/s: 18651.74 | kb/i: 0.02 -| create and destroy 1k entities with 2 components ... | - PASS | us: 232.02 | op/s: 4310.00 | kb/i: 0.06 -| create and destroy 1k entities with 3 components ... | - PASS | us: 329.49 | op/s: 3035.00 | kb/i: 0.10 -]] + for i = 1, #entities do + destroy(entities[i]) + end + end, function() + return {} + end) ---- ---- after chunks refactoring ---- +basics.describe_bench(string.format('create and destroy %d entities with 3 components / spawn_with', N), + ---@param entities evolved.id[] + function(entities) + local spawn_with = evo.spawn_with + local destroy = evo.destroy ---[[ lua 5.1 -| create and destroy 1k entities ... | - PASS | us: 254.45 | op/s: 3930.00 | kb/i: 0.04 -| create and destroy 1k entities with 1 component ... | - PASS | us: 897.32 | op/s: 1114.43 | kb/i: 0.36 -| create and destroy 1k entities with 2 components ... | - PASS | us: 1481.48 | op/s: 675.00 | kb/i: 0.49 -| create and destroy 1k entities with 3 components ... | - PASS | us: 2126.32 | op/s: 470.30 | kb/i: 0.90 -]] + local fragments = { F1, F2, F3 } + local components = { true, true, true } ---[[ luajit 2.1 -| create and destroy 1k entities ... | - PASS | us: 12.31 | op/s: 81248.76 | kb/i: 0.00 -| create and destroy 1k entities with 1 component ... | - PASS | us: 46.97 | op/s: 21288.56 | kb/i: 0.02 -| create and destroy 1k entities with 2 components ... | - PASS | us: 75.19 | op/s: 13300.00 | kb/i: 0.03 -| create and destroy 1k entities with 3 components ... | - PASS | us: 108.28 | op/s: 9235.00 | kb/i: 0.06 -]] + for i = 1, N do + entities[i] = spawn_with(fragments, components) + end + + for i = 1, #entities do + destroy(entities[i]) + end + end, function() + return {} + end) + +basics.describe_bench(string.format('create and destroy %d entities with 4 components / spawn_with', N), + ---@param entities evolved.id[] + function(entities) + local spawn_with = evo.spawn_with + local destroy = evo.destroy + + local fragments = { F1, F2, F3, F4 } + local components = { true, true, true, true } + + for i = 1, N do + entities[i] = spawn_with(fragments, components) + end + + for i = 1, #entities do + destroy(entities[i]) + end + end, function() + return {} + end) + +basics.describe_bench(string.format('create and destroy %d entities with 5 components / spawn_with', N), + ---@param entities evolved.id[] + function(entities) + local spawn_with = evo.spawn_with + local destroy = evo.destroy + + local fragments = { F1, F2, F3, F4, F5 } + local components = { true, true, true, true, true } + + for i = 1, N do + entities[i] = spawn_with(fragments, components) + end + + for i = 1, #entities do + destroy(entities[i]) + end + end, function() + return {} + end) + +print '----------------------------------------' diff --git a/develop/untests.lua b/develop/untests.lua index 93aacf4..aa8cb88 100644 --- a/develop/untests.lua +++ b/develop/untests.lua @@ -3153,66 +3153,66 @@ do evo.set(f3, evo.TAG) do - local e = evo.spawn() + local e = evo.spawn_with() assert(evo.is_alive(e) and evo.is_empty(e)) end do - local e = evo.spawn({}) + local e = evo.spawn_with({}) assert(evo.is_alive(e) and evo.is_empty(e)) end do - local e1 = evo.spawn({ f1 }) + local e1 = evo.spawn_with({ f1 }) assert(evo.has(e1, f1) and evo.get(e1, f1) == true) - local e2 = evo.spawn({ f1 }, {}) + local e2 = evo.spawn_with({ f1 }, {}) assert(evo.has(e2, f1) and evo.get(e2, f1) == true) - local e3 = evo.spawn({ f1 }, { 41 }) + local e3 = evo.spawn_with({ f1 }, { 41 }) assert(evo.has(e3, f1) and evo.get(e3, f1) == 41) end do - local e1 = evo.spawn({ f1, f2 }) + local e1 = evo.spawn_with({ f1, f2 }) assert(evo.has_all(e1, f1, f2)) assert(evo.get(e1, f1) == true and evo.get(e1, f2) == true) - local e2 = evo.spawn({ f1, f2 }, {}) + local e2 = evo.spawn_with({ f1, f2 }, {}) assert(evo.has_all(e2, f1, f2)) assert(evo.get(e2, f1) == true and evo.get(e2, f2) == true) - local e3 = evo.spawn({ f1, f2 }, { 41 }) + local e3 = evo.spawn_with({ f1, f2 }, { 41 }) assert(evo.has_all(e3, f1, f2)) assert(evo.get(e3, f1) == 41 and evo.get(e3, f2) == true) - local e4 = evo.spawn({ f1, f2 }, { nil, 42 }) + local e4 = evo.spawn_with({ f1, f2 }, { nil, 42 }) assert(evo.has_all(e4, f1, f2)) assert(evo.get(e4, f1) == true and evo.get(e4, f2) == 42) - local e5 = evo.spawn({ f1, f2 }, { 41, 42 }) + local e5 = evo.spawn_with({ f1, f2 }, { 41, 42 }) assert(evo.has_all(e5, f1, f2)) assert(evo.get(e5, f1) == 41 and evo.get(e5, f2) == 42) - local e6 = evo.spawn({ f1, f2 }, { 41, 42, 43 }) + local e6 = evo.spawn_with({ f1, f2 }, { 41, 42, 43 }) assert(evo.has_all(e6, f1, f2)) assert(evo.get(e6, f1) == 41 and evo.get(e6, f2) == 42) end do - local e1 = evo.spawn({ f3 }) + local e1 = evo.spawn_with({ f3 }) assert(evo.has(e1, f3)) assert(evo.get(e1, f3) == nil) - local e2 = evo.spawn({ f2, f3 }) + local e2 = evo.spawn_with({ f2, f3 }) assert(evo.has_all(e2, f2, f3)) assert(evo.get(e2, f2) == true and evo.get(e2, f3) == nil) - local e3 = evo.spawn({ f2, f3 }, { 42 }) + local e3 = evo.spawn_with({ f2, f3 }, { 42 }) assert(evo.has_all(e3, f2, f3)) assert(evo.get(e3, f2) == 42 and evo.get(e3, f3) == nil) - local e4 = evo.spawn({ f2, f3 }, { 42, 43, 44 }) + local e4 = evo.spawn_with({ f2, f3 }, { 42, 43, 44 }) assert(evo.has_all(e4, f2, f3)) assert(evo.get(e4, f2) == 42 and evo.get(e4, f3) == nil) end @@ -3225,66 +3225,66 @@ do evo.set(f3, evo.TAG) do - local e = evo.spawn() + local e = evo.spawn_with() assert(evo.is_alive(e) and evo.is_empty(e)) end do - local e = evo.spawn({}) + local e = evo.spawn_with({}) assert(evo.is_alive(e) and evo.is_empty(e)) end do - local e1 = evo.spawn({ f1 }) + local e1 = evo.spawn_with({ f1 }) assert(evo.has(e1, f1) and evo.get(e1, f1) == true) - local e2 = evo.spawn({ f1 }, {}) + local e2 = evo.spawn_with({ f1 }, {}) assert(evo.has(e2, f1) and evo.get(e2, f1) == true) - local e3 = evo.spawn({ f1 }, { 41 }) + local e3 = evo.spawn_with({ f1 }, { 41 }) assert(evo.has(e3, f1) and evo.get(e3, f1) == 41) end do - local e1 = evo.spawn({ f1, f2 }) + local e1 = evo.spawn_with({ f1, f2 }) assert(evo.has_all(e1, f1, f2)) assert(evo.get(e1, f1) == true and evo.get(e1, f2) == 21) - local e2 = evo.spawn({ f1, f2 }, {}) + local e2 = evo.spawn_with({ f1, f2 }, {}) assert(evo.has_all(e2, f1, f2)) assert(evo.get(e2, f1) == true and evo.get(e2, f2) == 21) - local e3 = evo.spawn({ f1, f2 }, { 41 }) + local e3 = evo.spawn_with({ f1, f2 }, { 41 }) assert(evo.has_all(e3, f1, f2)) assert(evo.get(e3, f1) == 41 and evo.get(e3, f2) == 21) - local e4 = evo.spawn({ f1, f2 }, { nil, 42 }) + local e4 = evo.spawn_with({ f1, f2 }, { nil, 42 }) assert(evo.has_all(e4, f1, f2)) assert(evo.get(e4, f1) == true and evo.get(e4, f2) == 42) - local e5 = evo.spawn({ f1, f2 }, { 41, 42 }) + local e5 = evo.spawn_with({ f1, f2 }, { 41, 42 }) assert(evo.has_all(e5, f1, f2)) assert(evo.get(e5, f1) == 41 and evo.get(e5, f2) == 42) - local e6 = evo.spawn({ f1, f2 }, { 41, 42, 43 }) + local e6 = evo.spawn_with({ f1, f2 }, { 41, 42, 43 }) assert(evo.has_all(e6, f1, f2)) assert(evo.get(e6, f1) == 41 and evo.get(e6, f2) == 42) end do - local e1 = evo.spawn({ f3 }) + local e1 = evo.spawn_with({ f3 }) assert(evo.has(e1, f3)) assert(evo.get(e1, f3) == nil) - local e2 = evo.spawn({ f2, f3 }) + local e2 = evo.spawn_with({ f2, f3 }) assert(evo.has_all(e2, f2, f3)) assert(evo.get(e2, f2) == 21 and evo.get(e2, f3) == nil) - local e3 = evo.spawn({ f2, f3 }, { 42 }) + local e3 = evo.spawn_with({ f2, f3 }, { 42 }) assert(evo.has_all(e3, f2, f3)) assert(evo.get(e3, f2) == 42 and evo.get(e3, f3) == nil) - local e4 = evo.spawn({ f2, f3 }, { 42, 43, 44 }) + local e4 = evo.spawn_with({ f2, f3 }, { 42, 43, 44 }) assert(evo.has_all(e4, f2, f3)) assert(evo.get(e4, f2) == 42 and evo.get(e4, f3) == nil) end @@ -3292,12 +3292,14 @@ end do local cf = evo.id() - local f1, f2 = evo.id(2) + local f1, f2, f3 = evo.id(3) evo.set(f1, cf) evo.set(f2, cf) + evo.set(f3, cf) evo.set(f2, evo.DEFAULT, 21) + evo.set(f3, evo.TAG) local set_count = 0 local insert_count = 0 @@ -3311,14 +3313,14 @@ do evo.batch_set(q, evo.ON_SET, function(e, f, c) last_set_entity = e - assert(f == f1 or f == f2) + assert(f == f1 or f == f2 or f == f3) last_set_component = c set_count = set_count + 1 end) evo.batch_set(q, evo.ON_INSERT, function(e, f, c) last_insert_entity = e - assert(f == f1 or f == f2) + assert(f == f1 or f == f2 or f == f3) last_insert_component = c insert_count = insert_count + 1 end) @@ -3331,7 +3333,7 @@ do set_count, insert_count = 0, 0 last_set_entity, last_set_component = 0, 0 last_insert_entity, last_insert_component = 0, 0 - local e = evo.spawn({ f1 }) + local e = evo.spawn_with({ f1 }) assert(set_count == 1 and insert_count == 1) assert(last_set_entity == e and last_set_component == true) assert(last_insert_entity == e and last_insert_component == true) @@ -3341,7 +3343,7 @@ do set_count, insert_count = 0, 0 last_set_entity, last_set_component = 0, 0 last_insert_entity, last_insert_component = 0, 0 - local e = evo.spawn({ f2 }) + local e = evo.spawn_with({ f2 }) assert(set_count == 1 and insert_count == 1) assert(last_set_entity == e and last_set_component == 21) assert(last_insert_entity == e and last_insert_component == 21) @@ -3351,11 +3353,31 @@ do set_count, insert_count = 0, 0 last_set_entity, last_set_component = 0, 0 last_insert_entity, last_insert_component = 0, 0 - local e = evo.spawn({ f1, f2 }) + local e = evo.spawn_with({ f1, f2 }) assert(set_count == 2 and insert_count == 2) assert(last_set_entity == e and last_set_component == 21) assert(last_insert_entity == e and last_insert_component == 21) end + + do + set_count, insert_count = 0, 0 + last_set_entity, last_set_component = 0, 0 + last_insert_entity, last_insert_component = 0, 0 + local e = evo.spawn_with({ f3 }, { 33 }) + assert(set_count == 1 and insert_count == 1) + assert(last_set_entity == e and last_set_component == nil) + assert(last_insert_entity == e and last_insert_component == nil) + end + + do + set_count, insert_count = 0, 0 + last_set_entity, last_set_component = 0, 0 + last_insert_entity, last_insert_component = 0, 0 + local e = evo.spawn_with({ f3, f2 }, { 33, 22 }) + assert(set_count == 2 and insert_count == 2) + assert(last_set_entity == e and last_set_component == nil) + assert(last_insert_entity == e and last_insert_component == nil) + end end do @@ -3469,12 +3491,14 @@ end do local cf = evo.id() - local f1, f2 = evo.id(2) + local f1, f2, f3 = evo.id(3) evo.set(f1, cf) evo.set(f2, cf) + evo.set(f3, cf) evo.set(f2, evo.DEFAULT, 22) + evo.set(f3, evo.TAG) local set_count = 0 local insert_count = 0 @@ -3488,14 +3512,14 @@ do evo.batch_set(q, evo.ON_SET, function(e, f, c) last_set_entity = e - assert(f == f1 or f == f2) + assert(f == f1 or f == f2 or f == f3) last_set_component = c set_count = set_count + 1 end) evo.batch_set(q, evo.ON_INSERT, function(e, f, c) last_insert_entity = e - assert(f == f1 or f == f2) + assert(f == f1 or f == f2 or f == f3) last_insert_component = c insert_count = insert_count + 1 end) @@ -3536,4 +3560,153 @@ do assert(last_set_entity == e and last_set_component == 22) assert(last_insert_entity == e and last_insert_component == 22) end + + do + set_count, insert_count = 0, 0 + last_set_entity, last_set_component = 0, 0 + last_insert_entity, last_insert_component = 0, 0 + local c = evo.chunk(f3) + local e = evo.spawn_at(c) + assert(set_count == 1 and insert_count == 1) + assert(last_set_entity == e and last_set_component == nil) + assert(last_insert_entity == e and last_insert_component == nil) + end + + do + set_count, insert_count = 0, 0 + last_set_entity, last_set_component = 0, 0 + last_insert_entity, last_insert_component = 0, 0 + local c = evo.chunk(f3, f2) + local e = evo.spawn_at(c, { f3, f2 }, { 33, 22 }) + assert(set_count == 2 and insert_count == 2) + assert(last_set_entity == e and last_set_component == nil) + assert(last_insert_entity == e and last_insert_component == nil) + end +end + +do + local f1, f2, f3, f4 = evo.id(4) + + evo.set(f3, evo.DEFAULT, 3) + evo.set(f4, evo.TAG) + + do + assert(evo.defer()) + local e, d = evo.spawn_with() + assert(evo.is_alive(e) and evo.is_empty(e)) + assert(not d) + assert(evo.commit()) + assert(evo.is_alive(e) and evo.is_empty(e)) + end + + do + assert(evo.defer()) + local e, d = evo.spawn_with({}) + assert(evo.is_alive(e) and evo.is_empty(e)) + assert(not d) + assert(evo.commit()) + assert(evo.is_alive(e) and evo.is_empty(e)) + end + + do + assert(evo.defer()) + local e1, d1 = evo.spawn_with({ f1 }) + assert(evo.is_alive(e1) and evo.is_empty(e1)) + assert(d1) + assert(evo.commit()) + assert(evo.is_alive(e1) and not evo.is_empty(e1)) + assert(evo.has(e1, f1) and evo.get(e1, f1) == true) + + assert(evo.defer()) + local e2, d2 = evo.spawn_with({ f1 }, {}) + assert(evo.is_alive(e2) and evo.is_empty(e2)) + assert(d2) + assert(evo.commit()) + assert(evo.is_alive(e2) and not evo.is_empty(e2)) + assert(evo.has(e2, f1) and evo.get(e2, f1) == true) + + assert(evo.defer()) + local e3, d3 = evo.spawn_with({ f1 }, { 41 }) + assert(evo.is_alive(e3) and evo.is_empty(e3)) + assert(d3) + assert(evo.commit()) + assert(evo.is_alive(e3) and not evo.is_empty(e3)) + assert(evo.has(e3, f1) and evo.get(e3, f1) == 41) + end + + do + assert(evo.defer()) + local e1, d1 = evo.spawn_with({ f1, f2 }) + assert(evo.is_alive(e1) and evo.is_empty(e1)) + assert(d1) + assert(evo.commit()) + assert(evo.is_alive(e1) and not evo.is_empty(e1)) + assert(evo.has(e1, f1) and evo.get(e1, f1) == true) + assert(evo.has(e1, f2) and evo.get(e1, f2) == true) + + assert(evo.defer()) + local e2, d2 = evo.spawn_with({ f1, f2 }, {}) + assert(evo.is_alive(e2) and evo.is_empty(e2)) + assert(d2) + assert(evo.commit()) + assert(evo.is_alive(e2) and not evo.is_empty(e2)) + assert(evo.has(e2, f1) and evo.get(e2, f1) == true) + assert(evo.has(e2, f2) and evo.get(e2, f2) == true) + + assert(evo.defer()) + local e3, d3 = evo.spawn_with({ f1, f2 }, { 41 }) + assert(evo.is_alive(e3) and evo.is_empty(e3)) + assert(d3) + assert(evo.commit()) + assert(evo.is_alive(e3) and not evo.is_empty(e3)) + assert(evo.has(e3, f1) and evo.get(e3, f1) == 41) + assert(evo.has(e3, f2) and evo.get(e3, f2) == true) + + assert(evo.defer()) + local e4, d4 = evo.spawn_with({ f1, f2 }, { nil, 42 }) + assert(evo.is_alive(e4) and evo.is_empty(e4)) + assert(d4) + assert(evo.commit()) + assert(evo.is_alive(e4) and not evo.is_empty(e4)) + assert(evo.has(e4, f1) and evo.get(e4, f1) == true) + assert(evo.has(e4, f2) and evo.get(e4, f2) == 42) + + assert(evo.defer()) + local e5, d5 = evo.spawn_with({ f1, f2 }, { 41, 42 }) + assert(evo.is_alive(e5) and evo.is_empty(e5)) + assert(d5) + assert(evo.commit()) + assert(evo.is_alive(e5) and not evo.is_empty(e5)) + assert(evo.has(e5, f1) and evo.get(e5, f1) == 41) + assert(evo.has(e5, f2) and evo.get(e5, f2) == 42) + + assert(evo.defer()) + local e6, d6 = evo.spawn_with({ f1, f2 }, { 41, 42, 43 }) + assert(evo.is_alive(e6) and evo.is_empty(e6)) + assert(d6) + assert(evo.commit()) + assert(evo.is_alive(e6) and not evo.is_empty(e6)) + assert(evo.has(e6, f1) and evo.get(e6, f1) == 41) + assert(evo.has(e6, f2) and evo.get(e6, f2) == 42) + end + + do + assert(evo.defer()) + local e1, d1 = evo.spawn_with({ f3, f4 }) + assert(evo.is_alive(e1) and evo.is_empty(e1)) + assert(d1) + assert(evo.commit()) + assert(evo.is_alive(e1) and not evo.is_empty(e1)) + assert(evo.has(e1, f3) and evo.get(e1, f3) == 3) + assert(evo.has(e1, f4) and evo.get(e1, f4) == nil) + + assert(evo.defer()) + local e2, d2 = evo.spawn_with({ f3, f4 }, { 33, 44 }) + assert(evo.is_alive(e2) and evo.is_empty(e2)) + assert(d2) + assert(evo.commit()) + assert(evo.is_alive(e2) and not evo.is_empty(e2)) + assert(evo.has(e2, f3) and evo.get(e2, f3) == 33) + assert(evo.has(e2, f4) and evo.get(e2, f4) == nil) + end end diff --git a/evolved.lua b/evolved.lua index c1fa210..7e85356 100644 --- a/evolved.lua +++ b/evolved.lua @@ -1387,6 +1387,165 @@ local function __detach_entity(entity) __structural_changes = __structural_changes + 1 end +---@param entity evolved.entity +---@param chunk evolved.chunk +---@param fragments evolved.fragment[] +---@param components evolved.component[] +local function __spawn_entity_at(entity, chunk, fragments, components) + local chunk_entities = chunk.__entities + local chunk_fragment_list = chunk.__fragment_list + local chunk_component_indices = chunk.__component_indices + local chunk_component_storages = chunk.__component_storages + + local place = #chunk_entities + 1 + chunk_entities[place] = entity + + for i = 1, #chunk_fragment_list do + local fragment = chunk_fragment_list[i] + local component_index = chunk_component_indices[fragment] + + if component_index then + local component_storage = chunk_component_storages[component_index] + + if chunk.__has_defaults_or_constructs then + local new_component = evolved.get(fragment, evolved.DEFAULT) + + if new_component == nil then + new_component = true + end + + component_storage[place] = new_component + else + local new_component = true + + component_storage[place] = new_component + end + end + end + + for i = 1, #fragments do + local fragment = fragments[i] + local component_index = chunk_component_indices[fragment] + + if component_index then + local component_storage = chunk_component_storages[component_index] + + if chunk.__has_defaults_or_constructs then + local new_component = components[i] + + if new_component == nil then + new_component = evolved.get(fragment, evolved.DEFAULT) + end + + if new_component == nil then + new_component = true + end + + component_storage[place] = new_component + else + local new_component = components[i] + + if new_component == nil then + new_component = true + end + + component_storage[place] = new_component + end + end + end + + if chunk.__has_set_or_insert_hooks then + for i = 1, #chunk_fragment_list do + local fragment = chunk_fragment_list[i] + local component_index = chunk_component_indices[fragment] + + if component_index then + local component_storage = chunk_component_storages[component_index] + + local new_component = component_storage[place] + + __fragment_call_set_and_insert_hooks(entity, fragment, new_component) + else + __fragment_call_set_and_insert_hooks(entity, fragment) + end + end + end + + local entity_index = entity % 0x100000 + __entity_chunks[entity_index] = chunk + __entity_places[entity_index] = place + + __structural_changes = __structural_changes + 1 +end + +---@param entity evolved.entity +---@param chunk evolved.chunk +---@param fragments evolved.fragment[] +---@param components evolved.component[] +local function __spawn_entity_with(entity, chunk, fragments, components) + local chunk_entities = chunk.__entities + local chunk_fragment_list = chunk.__fragment_list + local chunk_component_indices = chunk.__component_indices + local chunk_component_storages = chunk.__component_storages + + local place = #chunk_entities + 1 + chunk_entities[place] = entity + + for i = 1, #fragments do + local fragment = fragments[i] + local component_index = chunk_component_indices[fragment] + + if component_index then + local component_storage = chunk_component_storages[component_index] + + if chunk.__has_defaults_or_constructs then + local new_component = components[i] + + if new_component == nil then + new_component = evolved.get(fragment, evolved.DEFAULT) + end + + if new_component == nil then + new_component = true + end + + component_storage[place] = new_component + else + local new_component = components[i] + + if new_component == nil then + new_component = true + end + + component_storage[place] = new_component + end + end + end + + if chunk.__has_set_or_insert_hooks then + for i = 1, #chunk_fragment_list do + local fragment = chunk_fragment_list[i] + local component_index = chunk_component_indices[fragment] + + if component_index then + local component_storage = chunk_component_storages[component_index] + + local new_component = component_storage[place] + + __fragment_call_set_and_insert_hooks(entity, fragment, new_component) + else + __fragment_call_set_and_insert_hooks(entity, fragment) + end + end + end + + local entity_index = entity % 0x100000 + __entity_chunks[entity_index] = chunk + __entity_places[entity_index] = place + + __structural_changes = __structural_changes + 1 +end + --- --- --- @@ -1413,6 +1572,9 @@ local __defer_op = { batch_remove = 14, batch_clear = 15, batch_destroy = 16, + + spawn_entity_at = 17, + spawn_entity_with = 18, } ---@type table @@ -1525,6 +1687,26 @@ local __defer_ops = { evolved.batch_destroy(query) return 1 end, + [__defer_op.spawn_entity_at] = function(bytes, index) + local entity = bytes[index + 0] + local chunk = bytes[index + 1] + local fragments = bytes[index + 2] + local components = bytes[index + 3] + __spawn_entity_at(entity, chunk, fragments, components) + __release_table(__TABLE_POOL_TAG__FRAGMENT_LIST, fragments) + __release_table(__TABLE_POOL_TAG__COMPONENT_LIST, components) + return 4 + end, + [__defer_op.spawn_entity_with] = function(bytes, index) + local entity = bytes[index + 0] + local chunk = bytes[index + 1] + local fragments = bytes[index + 2] + local components = bytes[index + 3] + __spawn_entity_with(entity, chunk, fragments, components) + __release_table(__TABLE_POOL_TAG__FRAGMENT_LIST, fragments) + __release_table(__TABLE_POOL_TAG__COMPONENT_LIST, components) + return 4 + end, } ---@return boolean started @@ -1861,6 +2043,56 @@ local function __defer_batch_destroy(query) __defer_length = length + 2 end +---@param entity evolved.entity +---@param chunk evolved.chunk +---@param fragments evolved.fragment[] +---@param components evolved.component[] +local function __defer_spawn_entity_at(entity, chunk, fragments, components) + local fragment_count = #fragments + local fragment_list = __acquire_table(__TABLE_POOL_TAG__FRAGMENT_LIST, fragment_count, 0) + __table_move(fragments, 1, fragment_count, 1, fragment_list) + + local component_count = #components + local component_list = __acquire_table(__TABLE_POOL_TAG__COMPONENT_LIST, component_count, 0) + __table_move(components, 1, component_count, 1, component_list) + + local length = __defer_length + local bytecode = __defer_bytecode + + bytecode[length + 1] = __defer_op.spawn_entity_at + bytecode[length + 2] = entity + bytecode[length + 3] = chunk + bytecode[length + 4] = fragment_list + bytecode[length + 5] = component_list + + __defer_length = length + 5 +end + +---@param entity evolved.entity +---@param chunk evolved.chunk +---@param fragments evolved.fragment[] +---@param components evolved.component[] +local function __defer_spawn_entity_with(entity, chunk, fragments, components) + local fragment_count = #fragments + local fragment_list = __acquire_table(__TABLE_POOL_TAG__FRAGMENT_LIST, fragment_count, 0) + __table_move(fragments, 1, fragment_count, 1, fragment_list) + + local component_count = #components + local component_list = __acquire_table(__TABLE_POOL_TAG__COMPONENT_LIST, component_count, 0) + __table_move(components, 1, component_count, 1, component_list) + + local length = __defer_length + local bytecode = __defer_bytecode + + bytecode[length + 1] = __defer_op.spawn_entity_with + bytecode[length + 2] = entity + bytecode[length + 3] = chunk + bytecode[length + 4] = fragment_list + bytecode[length + 5] = component_list + + __defer_length = length + 5 +end + --- --- --- @@ -3510,95 +3742,6 @@ end --- --- ----@param fragments? evolved.fragment[] ----@param components? evolved.component[] ----@return evolved.entity entity ----@return boolean is_deferred -function evolved.spawn(fragments, components) - if not fragments then - fragments = __EMPTY_FRAGMENT_LIST - end - - if not components then - components = __EMPTY_COMPONENT_LIST - end - - local entity = evolved.id() - - local chunk = __chunk_fragment_list(fragments) - - if not chunk then - return entity, false - end - - if __defer_depth > 0 then - error("spawn cannot be deferred yet") - end - - __defer() - - do - local chunk_entities = chunk.__entities - local chunk_component_indices = chunk.__component_indices - local chunk_component_storages = chunk.__component_storages - - local place = #chunk_entities + 1 - chunk_entities[place] = entity - - for i = 1, #fragments do - local fragment = fragments[i] - local component_index = chunk_component_indices[fragment] - - if component_index then - local component_storage = chunk_component_storages[component_index] - - if chunk.__has_defaults_or_constructs then - local new_component = components[i] - - if new_component == nil then - new_component = evolved.get(fragment, evolved.DEFAULT) - end - - if new_component == nil then - new_component = true - end - - component_storage[place] = new_component - - if chunk.__has_set_or_insert_hooks then - __fragment_call_set_and_insert_hooks(entity, fragment, new_component) - end - else - local new_component = components[i] - - if new_component == nil then - new_component = true - end - - component_storage[place] = new_component - - if chunk.__has_set_or_insert_hooks then - __fragment_call_set_and_insert_hooks(entity, fragment, new_component) - end - end - else - if chunk.__has_set_or_insert_hooks then - __fragment_call_set_and_insert_hooks(entity, fragment) - end - end - end - - local entity_index = entity % 0x100000 - __entity_chunks[entity_index] = chunk - __entity_places[entity_index] = place - - __structural_changes = __structural_changes + 1 - end - - __defer_commit() - return entity, false -end - ---@param chunk? evolved.chunk ---@param fragments? evolved.fragment[] ---@param components? evolved.component[] @@ -3620,96 +3763,48 @@ function evolved.spawn_at(chunk, fragments, components) end if __defer_depth > 0 then - error("spawn cannot be deferred yet") + __defer_spawn_entity_at(entity, chunk, fragments, components) + return entity, true end __defer() do - local chunk_entities = chunk.__entities - local chunk_fragment_list = chunk.__fragment_list - local chunk_component_indices = chunk.__component_indices - local chunk_component_storages = chunk.__component_storages + __spawn_entity_at(entity, chunk, fragments, components) + end - local place = #chunk_entities + 1 - chunk_entities[place] = entity + __defer_commit() + return entity, false +end - for i = 1, #chunk_fragment_list do - local fragment = chunk_fragment_list[i] - local component_index = chunk_component_indices[fragment] +---@param fragments? evolved.fragment[] +---@param components? evolved.component[] +---@return evolved.entity entity +---@return boolean is_deferred +function evolved.spawn_with(fragments, components) + if not fragments then + fragments = __EMPTY_FRAGMENT_LIST + end - if component_index then - local component_storage = chunk_component_storages[component_index] + if not components then + components = __EMPTY_COMPONENT_LIST + end - if chunk.__has_defaults_or_constructs then - local new_component = evolved.get(fragment, evolved.DEFAULT) + local entity, chunk = evolved.id(), __chunk_fragment_list(fragments) - if new_component == nil then - new_component = true - end + if not chunk then + return entity, false + end - component_storage[place] = new_component - else - local new_component = true + if __defer_depth > 0 then + __defer_spawn_entity_with(entity, chunk, fragments, components) + return entity, true + end - component_storage[place] = new_component - end - end - end + __defer() - for i = 1, #fragments do - local fragment = fragments[i] - local component_index = chunk_component_indices[fragment] - - if component_index then - local component_storage = chunk_component_storages[component_index] - - if chunk.__has_defaults_or_constructs then - local new_component = components[i] - - if new_component == nil then - new_component = evolved.get(fragment, evolved.DEFAULT) - end - - if new_component == nil then - new_component = true - end - - component_storage[place] = new_component - else - local new_component = components[i] - - if new_component == nil then - new_component = true - end - - component_storage[place] = new_component - end - end - end - - if chunk.__has_set_or_insert_hooks then - for i = 1, #chunk_fragment_list do - local fragment = chunk_fragment_list[i] - local component_index = chunk_component_indices[fragment] - - if component_index then - local component_storage = chunk_component_storages[component_index] - - local new_component = component_storage[place] - - __fragment_call_set_and_insert_hooks(entity, fragment, new_component) - else - __fragment_call_set_and_insert_hooks(entity, fragment) - end - end - end - - local entity_index = entity % 0x100000 - __entity_chunks[entity_index] = chunk - __entity_places[entity_index] = place - - __structural_changes = __structural_changes + 1 + do + __spawn_entity_with(entity, chunk, fragments, components) end __defer_commit() @@ -3784,7 +3879,7 @@ function evolved_entity_builder:build() return evolved.id() end - local entity = evolved.spawn(fragment_list, component_list) + local entity = evolved.spawn_with(fragment_list, component_list) __release_table(__TABLE_POOL_TAG__FRAGMENT_LIST, fragment_list) __release_table(__TABLE_POOL_TAG__COMPONENT_LIST, component_list) @@ -3876,7 +3971,7 @@ function evolved_fragment_builder:build() return evolved.id() end - local fragment = evolved.spawn(fragment_list, component_list) + local fragment = evolved.spawn_with(fragment_list, component_list) __release_table(__TABLE_POOL_TAG__FRAGMENT_LIST, fragment_list) __release_table(__TABLE_POOL_TAG__COMPONENT_LIST, component_list) @@ -3990,7 +4085,7 @@ function evolved_query_builder:build() return evolved.id() end - local query = evolved.spawn(fragment_list, component_list) + local query = evolved.spawn_with(fragment_list, component_list) __release_table(__TABLE_POOL_TAG__FRAGMENT_LIST, fragment_list) __release_table(__TABLE_POOL_TAG__COMPONENT_LIST, component_list)