Skip to content

Commit ba31aa9

Browse files
committed
Cleanup testing slightly
1 parent 5c051eb commit ba31aa9

File tree

6 files changed

+1012
-1238
lines changed

6 files changed

+1012
-1238
lines changed

addons/observers.luau

Lines changed: 65 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,32 @@
11
local jecs = require("@jecs")
2-
local testkit = require("@testkit")
2+
3+
type Observer<T...> = {
4+
callback: (jecs.Entity) -> (),
5+
query: jecs.Query<T...>,
6+
}
7+
8+
export type PatchedWorld = jecs.World & {
9+
added: (PatchedWorld, jecs.Id, (e: jecs.Entity, id: jecs.Id, value: any) -> ()) -> (),
10+
removed: (PatchedWorld, jecs.Id, (e: jecs.Entity, id: jecs.Id) -> ()) -> (),
11+
changed: (PatchedWorld, jecs.Id, (e: jecs.Entity, id: jecs.Id) -> ()) -> (),
12+
observer: (PatchedWorld, Observer<any>) -> (),
13+
monitor: (PatchedWorld, Observer<any>) -> (),
14+
}
315

416
local function observers_new(world, description)
517
local query = description.query
618
local callback = description.callback
7-
local terms = query.filter_with
19+
local terms = query.filter_with :: { jecs.Id }
820
if not terms then
921
local ids = query.ids
1022
query.filter_with = ids
1123
terms = ids
1224
end
1325

14-
local entity_index = world.entity_index
15-
local function emplaced(entity)
26+
local entity_index = world.entity_index :: any
27+
local function emplaced(entity: jecs.Entity)
1628
local r = jecs.entity_index_try_get_fast(
17-
entity_index, entity)
29+
entity_index, entity :: any)
1830

1931
if not r then
2032
return
@@ -33,74 +45,60 @@ local function observers_new(world, description)
3345
end
3446
end
3547

36-
local function world_track(world, ...)
37-
local entity_index = world.entity_index
38-
local terms = { ... }
39-
local q_shim = { filter_with = terms }
40-
41-
local n = 0
42-
local dense_array = {}
43-
local sparse_array = {}
48+
local function monitors_new(world, description)
49+
local query = description.query
50+
local callback = description.callback
51+
local terms = query.filter_with :: { jecs.Id }
52+
if not terms then
53+
local ids = query.ids
54+
query.filter_with = ids
55+
terms = ids
56+
end
4457

45-
local function emplaced(entity)
58+
local entity_index = world.entity_index :: any
59+
local function emplaced(entity: jecs.Entity)
4660
local r = jecs.entity_index_try_get_fast(
47-
entity_index, entity)
61+
entity_index, entity :: any)
4862

4963
if not r then
5064
return
5165
end
5266

5367
local archetype = r.archetype
5468

55-
if jecs.query_match(q_shim :: any, archetype) then
56-
n += 1
57-
dense_array[n] = entity
58-
sparse_array[entity] = n
69+
if jecs.query_match(query, archetype) then
70+
callback(entity, jecs.OnAdd)
5971
end
6072
end
6173

62-
local function removed(entity)
63-
local i = sparse_array[entity]
64-
if i ~= n then
65-
dense_array[i] = dense_array[n]
74+
local function removed(entity: jecs.Entity, component: jecs.Id)
75+
local r = jecs.entity_index_try_get_fast(
76+
entity_index, entity :: any)
77+
78+
if not r then
79+
return
6680
end
6781

68-
dense_array[n] = nil
82+
local archetype = r.archetype
83+
84+
if jecs.query_match(query, archetype) then
85+
callback(entity, jecs.OnRemove)
86+
end
6987
end
7088

7189
for _, term in terms do
7290
world:added(term, emplaced)
73-
world:changed(term, emplaced)
91+
world:removed(term, removed)
7492
end
75-
76-
local function iter()
77-
local i = n
78-
return function()
79-
local row = i
80-
if row == 0 then
81-
return nil
82-
end
83-
i -= 1
84-
return dense_array[row] :: any
85-
end
86-
end
87-
88-
local it = {
89-
__iter = iter,
90-
without = function(self, ...)
91-
q_shim.filter_without = { ... }
92-
return self
93-
end
94-
}
95-
return setmetatable(it, it)
9693
end
9794

98-
local function observers_add(world)
95+
local function observers_add(world: jecs.World & { [string]: any }): PatchedWorld
9996
local signals = {
10097
added = {},
10198
emplaced = {},
10299
removed = {}
103100
}
101+
104102
world.added = function(_, component, fn)
105103
local listeners = signals.added[component]
106104
if not listeners then
@@ -109,7 +107,7 @@ local function observers_add(world)
109107
local idr = jecs.id_record_ensure(world, component)
110108
idr.hooks.on_add = function(entity)
111109
for _, listener in listeners do
112-
listener(entity)
110+
listener(entity, component)
113111
end
114112
end
115113
end
@@ -124,7 +122,7 @@ local function observers_add(world)
124122
local idr = jecs.id_record_ensure(world, component)
125123
idr.hooks.on_change = function(entity, value)
126124
for _, listener in listeners do
127-
listener(entity, value)
125+
listener(entity, component, value)
128126
end
129127
end
130128
end
@@ -139,7 +137,7 @@ local function observers_add(world)
139137
local idr = jecs.id_record_ensure(world, component)
140138
idr.hooks.on_remove = function(entity)
141139
for _, listener in listeners do
142-
listener(entity)
140+
listener(entity, component)
143141
end
144142
end
145143
end
@@ -148,10 +146,24 @@ local function observers_add(world)
148146

149147
world.signals = signals
150148

151-
world.track = world_track
152-
153149
world.observer = observers_new
150+
151+
world.monitor = monitors_new
152+
154153
return world
155154
end
156155

157-
return observers_add
156+
local world = jecs.world()
157+
158+
observers_add(world):observer({
159+
callback = function() end,
160+
query = world:query(1 :: any)
161+
})
162+
163+
observers_add(world):added(1 :: any, function()
164+
165+
end)
166+
167+
return {
168+
add = observers_add
169+
}

jecs.luau

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2487,9 +2487,9 @@ return {
24872487

24882488
ECS_ID_DELETE = ECS_ID_DELETE,
24892489

2490-
IS_PAIR = ECS_IS_PAIR,
2491-
pair_first = ecs_pair_first,
2492-
pair_second = ecs_pair_second,
2490+
IS_PAIR = (ECS_IS_PAIR :: any) :: <P, O>(pair: Pair<P, O>) -> boolean,
2491+
pair_first = (ecs_pair_first :: any) :: <P, O>(world: World, pair: Pair<P, O>) -> Id<P>,
2492+
pair_second = (ecs_pair_second :: any) :: <P, O>(world: World, pair: Pair<P, O>) -> Id<O>,
24932493
entity_index_get_alive = entity_index_get_alive,
24942494

24952495
archetype_append_to_records = archetype_append_to_records,

test/addons/observers.luau

Lines changed: 77 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,89 @@
11
local jecs = require("@jecs")
2-
local observers_add = require("@addons/observers")
2+
local testkit = require("@testkit")
3+
local test = testkit.test()
4+
local CASE, TEST, FINISH, CHECK = test.CASE, test.TEST, test.FINISH, test.CHECK
5+
local observers = require("@addons/observers")
6+
local observers_add = observers.add
37

4-
local world = jecs.world()
5-
observers_add(world)
68

7-
local A = world:component()
8-
local B = world:component()
9-
local C = world:component()
9+
TEST("addons/observers", function()
10+
local world = observers_add(jecs.world())
1011

11-
world:observer({
12-
query = world:query(),
13-
callback = function(entity)
14-
buf ..= debug.info(2, "sl")
12+
13+
do CASE "Ensure ordering between signals and observers"
14+
local A = world:component()
15+
local B = world:component()
16+
17+
local count = 0
18+
local function counter()
19+
count += 1
20+
end
21+
world:observer({
22+
callback = counter,
23+
query = world:query(A, B),
24+
})
25+
26+
world:added(A, counter)
27+
world:added(A, counter)
28+
29+
world:removed(A, counter)
30+
31+
local e = world:entity()
32+
world:add(e, A)
33+
CHECK(count == 2)
34+
35+
world:add(e, B)
36+
CHECK(count == 3)
37+
38+
world:remove(e, A)
39+
CHECK(count == 4)
1540
end
16-
})
1741

18-
local i = 0
19-
world:added(A, function(entity)
20-
assert(i == 0)
21-
i += 1
22-
end)
23-
world:added(A, function(entity)
24-
assert(i == 1)
25-
i += 1
26-
end)
42+
do CASE "Rematch entities in observers"
43+
local A = world:component()
2744

28-
world:removed(A, function(entity)
29-
assert(false)
30-
end)
45+
local count = 0
46+
local function counter()
47+
count += 1
48+
end
49+
world:observer({
50+
query = world:query(A),
51+
callback = counter
52+
})
3153

32-
local observer = world:observer({
33-
query = world:query(A, B),
34-
callback = function(entity)
35-
assert(i == 2)
36-
i += 1
54+
local e = world:entity()
55+
world:set(e, A, true)
56+
CHECK(count == 1)
57+
world:remove(e, A)
58+
CHECK(count == 1)
59+
world:set(e, A, true)
60+
CHECK(count == 2)
61+
world:set(e, A, true)
62+
CHECK(count == 3)
3763
end
38-
})
3964

65+
do CASE "Don't report changed components in monitor"
66+
local A = world:component()
67+
local count = 0
68+
local function counter()
69+
count += 1
70+
end
4071

41-
local e = world:entity()
42-
world:add(e, A)
43-
assert(i == 2)
72+
world:monitor({
73+
query = world:query(A),
74+
callback = counter
75+
})
76+
77+
local e = world:entity()
78+
world:set(e, A, true)
79+
CHECK(count == 1)
80+
world:remove(e, A)
81+
CHECK(count == 2)
82+
world:set(e, A, true)
83+
CHECK(count == 3)
84+
world:set(e, A, true)
85+
CHECK(count == 3)
86+
end
87+
end)
4488

45-
world:add(e, B)
46-
assert(i == 3)
89+
return FINISH()

0 commit comments

Comments
 (0)