Skip to content

Commit 4632a61

Browse files
committed
don't match chunks with hidden fragments (while they are not explicit)
1 parent bea5058 commit 4632a61

3 files changed

Lines changed: 244 additions & 36 deletions

File tree

develop/fuzzing/hidden_fuzz.lua

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
local evo = require 'evolved'
2+
3+
evo.debug_mode(true)
4+
5+
---
6+
---
7+
---
8+
---
9+
---
10+
11+
local __table_unpack = (function()
12+
---@diagnostic disable-next-line: deprecated
13+
return table.unpack or unpack
14+
end)()
15+
16+
---
17+
---
18+
---
19+
---
20+
---
21+
22+
local all_entity_list = {} ---@type evolved.entity[]
23+
24+
for i = 1, math.random(1, 10) do
25+
local entity = evo.id()
26+
all_entity_list[i] = entity
27+
end
28+
29+
for _, entity in ipairs(all_entity_list) do
30+
for _ = 0, math.random(0, #all_entity_list) do
31+
local fragment = all_entity_list[math.random(1, #all_entity_list)]
32+
evo.set(entity, fragment)
33+
end
34+
35+
if math.random(1, 5) == 1 then
36+
evo.set(entity, evo.HIDDEN)
37+
end
38+
end
39+
40+
---
41+
---
42+
---
43+
---
44+
---
45+
46+
for _ = 1, 100 do
47+
local include_set = {} ---@type table<evolved.entity, integer>
48+
local include_list = {} ---@type evolved.entity[]
49+
local include_count = 0
50+
51+
for _ = 1, math.random(1, #all_entity_list) do
52+
local include = all_entity_list[math.random(1, #all_entity_list)]
53+
54+
if not include_set[include] then
55+
include_count = include_count + 1
56+
include_set[include] = include_count
57+
include_list[include_count] = include
58+
end
59+
end
60+
61+
local q = evo.builder():include(__table_unpack(include_list)):spawn()
62+
63+
for chunk in evo.execute(q) do
64+
local fragment_list, fragment_count = chunk:fragments()
65+
for i = 1, fragment_count do
66+
local fragment = fragment_list[i]
67+
assert(include_set[fragment] or not evo.has(fragment, evo.HIDDEN))
68+
end
69+
end
70+
71+
evo.destroy(q)
72+
end
73+
74+
---
75+
---
76+
---
77+
---
78+
---
79+
80+
evo.destroy(__table_unpack(all_entity_list))
81+
evo.collect_garbage()

develop/untests.lua

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6235,3 +6235,87 @@ do
62356235
assert(evo.has(e, f3) and evo.get(e, f3) == 3)
62366236
end
62376237
end
6238+
6239+
do
6240+
local f1, f2, f3 = evo.id(3)
6241+
6242+
evo.set(f2, evo.HIDDEN)
6243+
6244+
do
6245+
local p = evo.spawn { [f1] = 11, [f2] = 22, [f3] = 33 }
6246+
local e = evo.clone(p)
6247+
6248+
assert(evo.has(p, f1) and evo.get(p, f1) == 11)
6249+
assert(evo.has(p, f2) and evo.get(p, f2) == 22)
6250+
assert(evo.has(p, f3) and evo.get(p, f3) == 33)
6251+
6252+
assert(evo.has(e, f1) and evo.get(e, f1) == 11)
6253+
assert(not evo.has(e, f2) and evo.get(e, f2) == nil)
6254+
assert(evo.has(e, f3) and evo.get(e, f3) == 33)
6255+
end
6256+
end
6257+
6258+
do
6259+
local f1, f2 = evo.id(2)
6260+
6261+
local p = evo.builder():prefab():set(f1, 11):set(f2, 22):spawn()
6262+
local e = evo.clone(p)
6263+
6264+
do
6265+
local q = evo.builder():include(f1, f2):spawn()
6266+
local iter, state = evo.execute(q)
6267+
local chunk, entity_list, entity_count = iter(state)
6268+
assert(chunk and entity_list and entity_count)
6269+
assert(chunk == evo.chunk(f1, f2))
6270+
assert(entity_count == 1 and entity_list[1] == e)
6271+
end
6272+
6273+
do
6274+
local q = evo.builder():exclude(f1):spawn()
6275+
6276+
for c in evo.execute(q) do
6277+
local fs, fc = c:fragments()
6278+
for i = 1, fc do assert(not evo.has(fs[i], evo.HIDDEN)) end
6279+
end
6280+
end
6281+
6282+
do
6283+
local q = evo.builder():spawn()
6284+
6285+
for c in evo.execute(q) do
6286+
local fs, fc = c:fragments()
6287+
for i = 1, fc do assert(not evo.has(fs[i], evo.HIDDEN)) end
6288+
end
6289+
end
6290+
end
6291+
6292+
do
6293+
local f1, f2 = evo.id(2)
6294+
6295+
evo.set(f2, evo.HIDDEN)
6296+
6297+
local e1 = evo.builder():set(f1, 11):spawn()
6298+
local e2 = evo.builder():set(f1, 11):set(f2, 22):spawn()
6299+
6300+
do
6301+
local q = evo.builder():include(f1):spawn()
6302+
local iter, state = evo.execute(q)
6303+
local chunk, entity_list, entity_count = iter(state)
6304+
assert(chunk and entity_list and entity_count)
6305+
assert(chunk == evo.chunk(f1))
6306+
assert(entity_count == 1 and entity_list[1] == e1)
6307+
chunk, entity_list, entity_count = iter(state)
6308+
assert(not chunk and not entity_list and not entity_count)
6309+
end
6310+
6311+
do
6312+
local q = evo.builder():include(f1, f2):spawn()
6313+
local iter, state = evo.execute(q)
6314+
local chunk, entity_list, entity_count = iter(state)
6315+
assert(chunk and entity_list and entity_count)
6316+
assert(chunk == evo.chunk(f1, f2))
6317+
assert(entity_count == 1 and entity_list[1] == e2)
6318+
chunk, entity_list, entity_count = iter(state)
6319+
assert(not chunk and not entity_list and not entity_count)
6320+
end
6321+
end

evolved.lua

Lines changed: 79 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,8 @@ local __query_sorted_excludes = {} ---@type table<evolved.query, evolved.assoc_l
124124
---@field package __has_assign_hooks boolean
125125
---@field package __has_insert_hooks boolean
126126
---@field package __has_remove_hooks boolean
127+
---@field package __has_hidden_major boolean
128+
---@field package __has_hidden_minors boolean
127129
---@field package __has_hidden_fragments boolean
128130
local __chunk_mt = {}
129131
__chunk_mt.__index = __chunk_mt
@@ -619,22 +621,18 @@ local function __execute_iterator(execute_state)
619621
local chunk_child_list = chunk.__child_list
620622
local chunk_child_count = chunk.__child_count
621623

622-
if exclude_set then
623-
for i = 1, chunk_child_count do
624-
local chunk_child = chunk_child_list[i]
625-
local chunk_child_fragment = chunk_child.__fragment
624+
for i = 1, chunk_child_count do
625+
local chunk_child = chunk_child_list[i]
626+
local chunk_child_fragment = chunk_child.__fragment
626627

627-
if not exclude_set[chunk_child_fragment] then
628-
chunk_stack_size = chunk_stack_size + 1
629-
chunk_stack[chunk_stack_size] = chunk_child
630-
end
631-
end
632-
else
633-
__lua_table_move(
634-
chunk_child_list, 1, chunk_child_count,
635-
chunk_stack_size + 1, chunk_stack)
628+
local is_chunk_child_matched =
629+
(not chunk_child.__has_hidden_major) and
630+
(not exclude_set or not exclude_set[chunk_child_fragment])
636631

637-
chunk_stack_size = chunk_stack_size + chunk_child_count
632+
if is_chunk_child_matched then
633+
chunk_stack_size = chunk_stack_size + 1
634+
chunk_stack[chunk_stack_size] = chunk_child
635+
end
638636
end
639637

640638
local chunk_entity_list = chunk.__entity_list
@@ -693,6 +691,12 @@ local __DESTROY_POLICY_REMOVE_FRAGMENT = __acquire_id()
693691
---
694692

695693
local __safe_tbls = {
694+
---@type evolved.entity[]
695+
__EMPTY_ENTITY_LIST = __lua_setmetatable({}, {
696+
__tostring = function() return 'empty entity list' end,
697+
__newindex = function() __error_fmt 'attempt to modify empty entity list' end
698+
}),
699+
696700
---@type table<evolved.fragment, integer>
697701
__EMPTY_FRAGMENT_SET = __lua_setmetatable({}, {
698702
__tostring = function() return 'empty fragment set' end,
@@ -1002,20 +1006,21 @@ local function __new_chunk(chunk_parent, chunk_fragment)
10021006
---@type evolved.fragment[]
10031007
local chunk_component_fragments = {}
10041008

1005-
local has_setup_hooks = (chunk_parent and chunk_parent.__has_setup_hooks)
1009+
local has_setup_hooks = (chunk_parent ~= nil and chunk_parent.__has_setup_hooks)
10061010
or __evolved_has_any(chunk_fragment, __DEFAULT, __DUPLICATE)
10071011

1008-
local has_assign_hooks = (chunk_parent and chunk_parent.__has_assign_hooks)
1012+
local has_assign_hooks = (chunk_parent ~= nil and chunk_parent.__has_assign_hooks)
10091013
or __evolved_has_any(chunk_fragment, __ON_SET, __ON_ASSIGN)
10101014

1011-
local has_insert_hooks = (chunk_parent and chunk_parent.__has_insert_hooks)
1015+
local has_insert_hooks = (chunk_parent ~= nil and chunk_parent.__has_insert_hooks)
10121016
or __evolved_has_any(chunk_fragment, __ON_SET, __ON_INSERT)
10131017

1014-
local has_remove_hooks = (chunk_parent and chunk_parent.__has_remove_hooks)
1018+
local has_remove_hooks = (chunk_parent ~= nil and chunk_parent.__has_remove_hooks)
10151019
or __evolved_has(chunk_fragment, __ON_REMOVE)
10161020

1017-
local has_hidden_fragments = (chunk_parent and chunk_parent.__has_hidden_fragments)
1018-
or __evolved_has(chunk_fragment, __HIDDEN)
1021+
local has_hidden_major = __evolved_has(chunk_fragment, __HIDDEN)
1022+
local has_hidden_minors = chunk_parent ~= nil and chunk_parent.__has_hidden_fragments
1023+
local has_hidden_fragments = has_hidden_major or has_hidden_minors
10191024

10201025
---@type evolved.chunk
10211026
local chunk = __lua_setmetatable({
@@ -1040,6 +1045,8 @@ local function __new_chunk(chunk_parent, chunk_fragment)
10401045
__has_assign_hooks = has_assign_hooks,
10411046
__has_insert_hooks = has_insert_hooks,
10421047
__has_remove_hooks = has_remove_hooks,
1048+
__has_hidden_major = has_hidden_major,
1049+
__has_hidden_minors = has_hidden_minors,
10431050
__has_hidden_fragments = has_hidden_fragments,
10441051
}, __chunk_mt)
10451052

@@ -1237,18 +1244,26 @@ local function __chunk_without_hidden_fragments(chunk)
12371244
return chunk
12381245
end
12391246

1240-
local chunk_fragment_list = chunk.__fragment_list
1241-
local chunk_fragment_count = chunk.__fragment_count
1247+
while chunk and chunk.__has_hidden_major do
1248+
chunk = chunk.__parent
1249+
end
1250+
1251+
local new_chunk = nil
1252+
1253+
if chunk then
1254+
local chunk_fragment_list = chunk.__fragment_list
1255+
local chunk_fragment_count = chunk.__fragment_count
12421256

1243-
for i = chunk_fragment_count, 1, -1 do
1244-
local fragment = chunk_fragment_list[i]
1257+
for i = 1, chunk_fragment_count do
1258+
local fragment = chunk_fragment_list[i]
12451259

1246-
if __evolved_has(fragment, __HIDDEN) then
1247-
chunk = __chunk_without_fragment(chunk, fragment)
1260+
if not __evolved_has(fragment, __HIDDEN) then
1261+
new_chunk = __chunk_with_fragment(new_chunk, fragment)
1262+
end
12481263
end
12491264
end
12501265

1251-
return chunk
1266+
return new_chunk
12521267
end
12531268

12541269
---
@@ -4329,6 +4344,7 @@ function __evolved_execute(query)
43294344
local chunk_stack_size = 0
43304345

43314346
local query_includes = __query_sorted_includes[query]
4347+
local query_include_set = query_includes and query_includes.__item_set --[[@as table<evolved.fragment, integer>]]
43324348
local query_include_list = query_includes and query_includes.__item_list --[=[@as evolved.fragment[]]=]
43334349
local query_include_count = query_includes and query_includes.__item_count or 0 --[[@as integer]]
43344350

@@ -4353,22 +4369,45 @@ function __evolved_execute(query)
43534369
(query_exclude_count == 0 or not __chunk_has_any_fragment_list(
43544370
major_chunk, query_exclude_list, query_exclude_count))
43554371

4372+
if is_major_chunk_matched and major_chunk.__has_hidden_minors then
4373+
local major_chunk_fragment_list = major_chunk.__fragment_list
4374+
local major_chunk_fragment_count = major_chunk.__fragment_count
4375+
4376+
for major_chunk_fragment_index = 1, major_chunk_fragment_count - 1 do
4377+
local major_chunk_fragment = major_chunk_fragment_list[major_chunk_fragment_index]
4378+
4379+
if not query_include_set[major_chunk_fragment] and __evolved_has(major_chunk_fragment, __HIDDEN) then
4380+
is_major_chunk_matched = false
4381+
break
4382+
end
4383+
end
4384+
end
4385+
43564386
if is_major_chunk_matched then
43574387
chunk_stack_size = chunk_stack_size + 1
43584388
chunk_stack[chunk_stack_size] = major_chunk
43594389
end
43604390
end
43614391
elseif query_exclude_count > 0 then
43624392
for root_fragment, root_chunk in __lua_next, __root_chunks do
4363-
if not query_exclude_set[root_fragment] then
4393+
local is_root_chunk_matched =
4394+
not root_chunk.__has_hidden_major and
4395+
not query_exclude_set[root_fragment]
4396+
4397+
if is_root_chunk_matched then
43644398
chunk_stack_size = chunk_stack_size + 1
43654399
chunk_stack[chunk_stack_size] = root_chunk
43664400
end
43674401
end
43684402
else
43694403
for _, root_chunk in __lua_next, __root_chunks do
4370-
chunk_stack_size = chunk_stack_size + 1
4371-
chunk_stack[chunk_stack_size] = root_chunk
4404+
local is_root_chunk_matched =
4405+
not root_chunk.__has_hidden_major
4406+
4407+
if is_root_chunk_matched then
4408+
chunk_stack_size = chunk_stack_size + 1
4409+
chunk_stack[chunk_stack_size] = root_chunk
4410+
end
43724411
end
43734412
end
43744413

@@ -5048,25 +5087,29 @@ end
50485087
local function __update_chunk_caches_trace(chunk)
50495088
local chunk_parent, chunk_fragment = chunk.__parent, chunk.__fragment
50505089

5051-
local has_setup_hooks = (chunk_parent and chunk_parent.__has_setup_hooks)
5090+
local has_setup_hooks = (chunk_parent ~= nil and chunk_parent.__has_setup_hooks)
50525091
or __evolved_has_any(chunk_fragment, __DEFAULT, __DUPLICATE)
50535092

5054-
local has_assign_hooks = (chunk_parent and chunk_parent.__has_assign_hooks)
5093+
local has_assign_hooks = (chunk_parent ~= nil and chunk_parent.__has_assign_hooks)
50555094
or __evolved_has_any(chunk_fragment, __ON_SET, __ON_ASSIGN)
50565095

5057-
local has_insert_hooks = (chunk_parent and chunk_parent.__has_insert_hooks)
5096+
local has_insert_hooks = (chunk_parent ~= nil and chunk_parent.__has_insert_hooks)
50585097
or __evolved_has_any(chunk_fragment, __ON_SET, __ON_INSERT)
50595098

5060-
local has_remove_hooks = (chunk_parent and chunk_parent.__has_remove_hooks)
5099+
local has_remove_hooks = (chunk_parent ~= nil and chunk_parent.__has_remove_hooks)
50615100
or __evolved_has(chunk_fragment, __ON_REMOVE)
50625101

5063-
local has_hidden_fragments = (chunk_parent and chunk_parent.__has_hidden_fragments)
5064-
or __evolved_has(chunk_fragment, __HIDDEN)
5102+
local has_hidden_major = __evolved_has(chunk_fragment, __HIDDEN)
5103+
local has_hidden_minors = chunk_parent ~= nil and chunk_parent.__has_hidden_fragments
5104+
local has_hidden_fragments = has_hidden_major or has_hidden_minors
50655105

50665106
chunk.__has_setup_hooks = has_setup_hooks
50675107
chunk.__has_assign_hooks = has_assign_hooks
50685108
chunk.__has_insert_hooks = has_insert_hooks
50695109
chunk.__has_remove_hooks = has_remove_hooks
5110+
5111+
chunk.__has_hidden_major = has_hidden_major
5112+
chunk.__has_hidden_minors = has_hidden_minors
50705113
chunk.__has_hidden_fragments = has_hidden_fragments
50715114

50725115
return true

0 commit comments

Comments
 (0)