mirror of
https://github.com/Ukendio/jecs.git
synced 2025-04-25 09:30:03 +00:00
Compare commits
1 commit
418fe6b08d
...
de2bb07dd7
Author | SHA1 | Date | |
---|---|---|---|
|
de2bb07dd7 |
2 changed files with 61 additions and 323 deletions
347
jecs.luau
347
jecs.luau
|
@ -170,7 +170,7 @@ local function _STRIP_GENERATION(e: i53): i24
|
|||
end
|
||||
|
||||
local function ECS_PAIR(pred: i53, obj: i53): i53
|
||||
return ECS_COMBINE(ECS_ENTITY_T_LO(pred), ECS_ENTITY_T_LO(obj)) + FLAGS_ADD(--[[isPair]] true) :: i53
|
||||
return ECS_COMBINE(ECS_ENTITY_T_LO(obj), ECS_ENTITY_T_LO(pred)) + FLAGS_ADD(--[[isPair]] true) :: i53
|
||||
end
|
||||
|
||||
local function entity_index_try_get_any(entity_index: EntityIndex, entity: number): Record?
|
||||
|
@ -244,33 +244,26 @@ end
|
|||
|
||||
-- ECS_PAIR_FIRST, gets the relationship target / obj / HIGH bits
|
||||
local function ecs_pair_first(world, e)
|
||||
return entity_index_get_alive(world.entity_index, ECS_ENTITY_T_LO(e))
|
||||
return entity_index_get_alive(world.entity_index, ECS_ENTITY_T_HI(e))
|
||||
end
|
||||
|
||||
-- ECS_PAIR_SECOND gets the relationship / pred / LOW bits
|
||||
local function ecs_pair_second(world, e)
|
||||
return entity_index_get_alive(world.entity_index, ECS_ENTITY_T_HI(e))
|
||||
return entity_index_get_alive(world.entity_index, ECS_ENTITY_T_LO(e))
|
||||
end
|
||||
|
||||
local function query_match(query, archetype: Archetype)
|
||||
local function query_match(terms: { {i53 }}, archetype: Archetype)
|
||||
local records = archetype.records
|
||||
local with = query.filter_with
|
||||
|
||||
for _, id in with do
|
||||
if not records[id] then
|
||||
for _, term in terms do
|
||||
local id = term[1]
|
||||
local out = term[2]
|
||||
local has = records[id] ~= nil
|
||||
if has ~= not out then
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
local without = query.filter_without
|
||||
if not without then
|
||||
for _, id in without do
|
||||
if records[id] then
|
||||
return false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
|
@ -624,7 +617,7 @@ local function archetype_create(world: World, id_types: { i24 }, ty, prev: i53?)
|
|||
continue
|
||||
end
|
||||
for _, observer in observer_list do
|
||||
if query_match(observer.query, archetype) then
|
||||
if query_match(observer.terms, archetype) then
|
||||
observer.callback(archetype)
|
||||
end
|
||||
end
|
||||
|
@ -1057,7 +1050,7 @@ local function archetype_destroy(world: World, archetype: Archetype)
|
|||
continue
|
||||
end
|
||||
for _, observer in observer_list do
|
||||
if query_match(observer.query, archetype) then
|
||||
if query_match(observer.terms, archetype) then
|
||||
observer.callback(archetype)
|
||||
end
|
||||
end
|
||||
|
@ -1231,7 +1224,7 @@ local EMPTY_QUERY = {
|
|||
|
||||
setmetatable(EMPTY_QUERY, EMPTY_QUERY)
|
||||
|
||||
local function query_iter_init(query: QueryInner): () -> (number, ...any)
|
||||
local function query_iter_init(query): () -> (number, ...any)
|
||||
local world_query_iter_next
|
||||
|
||||
local compatible_archetypes = query.compatible_archetypes
|
||||
|
@ -1310,7 +1303,7 @@ local function query_iter_init(query: QueryInner): () -> (number, ...any)
|
|||
i = #entities
|
||||
entityId = entities[i]
|
||||
columns = archetype.columns
|
||||
records = archetype.records
|
||||
local records = archetype.records
|
||||
a = columns[records[A].column]
|
||||
end
|
||||
|
||||
|
@ -1333,7 +1326,7 @@ local function query_iter_init(query: QueryInner): () -> (number, ...any)
|
|||
i = #entities
|
||||
entityId = entities[i]
|
||||
columns = archetype.columns
|
||||
records = archetype.records
|
||||
local records = archetype.records
|
||||
a = columns[records[A].column]
|
||||
b = columns[records[B].column]
|
||||
end
|
||||
|
@ -1357,7 +1350,7 @@ local function query_iter_init(query: QueryInner): () -> (number, ...any)
|
|||
i = #entities
|
||||
entityId = entities[i]
|
||||
columns = archetype.columns
|
||||
records = archetype.records
|
||||
local records = archetype.records
|
||||
a = columns[records[A].column]
|
||||
b = columns[records[B].column]
|
||||
c = columns[records[C].column]
|
||||
|
@ -1382,7 +1375,7 @@ local function query_iter_init(query: QueryInner): () -> (number, ...any)
|
|||
i = #entities
|
||||
entityId = entities[i]
|
||||
columns = archetype.columns
|
||||
records = archetype.records
|
||||
local records = archetype.records
|
||||
a = columns[records[A].column]
|
||||
b = columns[records[B].column]
|
||||
c = columns[records[C].column]
|
||||
|
@ -1409,7 +1402,7 @@ local function query_iter_init(query: QueryInner): () -> (number, ...any)
|
|||
i = #entities
|
||||
entityId = entities[i]
|
||||
columns = archetype.columns
|
||||
records = archetype.records
|
||||
local records = archetype.records
|
||||
|
||||
if not F then
|
||||
a = columns[records[A].column]
|
||||
|
@ -1479,8 +1472,13 @@ local function query_iter(query): () -> (number, ...any)
|
|||
end
|
||||
|
||||
local function query_without(query: QueryInner, ...: i53)
|
||||
local filters: { without: { i53 } } = query.filters :: any
|
||||
local without = { ... }
|
||||
query.filter_without = without
|
||||
if not filters then
|
||||
filters = {}
|
||||
query.filters = filters :: any
|
||||
end
|
||||
filters.without = without
|
||||
local compatible_archetypes = query.compatible_archetypes
|
||||
for i = #compatible_archetypes, 1, -1 do
|
||||
local archetype = compatible_archetypes[i]
|
||||
|
@ -1508,11 +1506,15 @@ local function query_without(query: QueryInner, ...: i53)
|
|||
return query :: any
|
||||
end
|
||||
|
||||
local function query_with(query: QueryInner, ...: i53)
|
||||
local function query_with(query: QueryInner, ...)
|
||||
local compatible_archetypes = query.compatible_archetypes
|
||||
local filters: { with: { i53 } } = query.filters :: any
|
||||
local with = { ... }
|
||||
query.filter_with = with
|
||||
|
||||
if not filters then
|
||||
filters = {}
|
||||
query.filters = filters :: any
|
||||
end
|
||||
filters.with = with
|
||||
for i = #compatible_archetypes, 1, -1 do
|
||||
local archetype = compatible_archetypes[i]
|
||||
local records = archetype.records
|
||||
|
@ -1586,270 +1588,34 @@ local function query_cached(query: QueryInner)
|
|||
archetypes[n] = nil
|
||||
end
|
||||
|
||||
local with = query.filter_with
|
||||
local ids = query.ids
|
||||
if with then
|
||||
table.move(ids, 1, #ids, #with, with)
|
||||
else
|
||||
query.filter_with = ids
|
||||
local terms = {}
|
||||
|
||||
for _, id in query.ids do
|
||||
table.insert(terms, { id })
|
||||
end
|
||||
local filters = query.filters
|
||||
if filters then
|
||||
local with = filters.with
|
||||
if with then
|
||||
for _, id in with do
|
||||
table.insert(terms, { id })
|
||||
end
|
||||
end
|
||||
local without = filters.without
|
||||
if without then
|
||||
for _, id in without do
|
||||
table.insert(terms, { id, true })
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local observer_for_create = { query = query, callback = on_create_callback }
|
||||
local observer_for_delete = { query = query, callback = on_delete_callback }
|
||||
local observer_for_create = { query = query, callback = on_create_callback, terms = terms }
|
||||
local observer_for_delete = { query = query, callback = on_delete_callback, terms = terms }
|
||||
|
||||
table.insert(query_cache_on_create, observer_for_create)
|
||||
table.insert(query_cache_on_delete, observer_for_delete)
|
||||
|
||||
local compatible_archetypes = query.compatible_archetypes
|
||||
local lastArchetype = 1
|
||||
|
||||
local A, B, C, D, E, F, G, H, I = unpack(ids)
|
||||
local a: Column, b: Column, c: Column, d: Column
|
||||
local e: Column, f: Column, g: Column, h: Column
|
||||
|
||||
local world_query_iter_next
|
||||
local columns: { Column }
|
||||
local entities: { i53 }
|
||||
local i: number
|
||||
local archetype: Archetype
|
||||
local records: { ArchetypeRecord }
|
||||
|
||||
if not B then
|
||||
function world_query_iter_next(): any
|
||||
local entityId = entities[i]
|
||||
while entityId == nil do
|
||||
lastArchetype += 1
|
||||
archetype = compatible_archetypes[lastArchetype]
|
||||
if not archetype then
|
||||
return nil
|
||||
end
|
||||
|
||||
entities = archetype.entities
|
||||
i = #entities
|
||||
entityId = entities[i]
|
||||
columns = archetype.columns
|
||||
records = archetype.records
|
||||
a = columns[records[A].column]
|
||||
end
|
||||
|
||||
local row = i
|
||||
i -= 1
|
||||
|
||||
return entityId, a[row]
|
||||
end
|
||||
elseif not C then
|
||||
function world_query_iter_next(): any
|
||||
local entityId = entities[i]
|
||||
while entityId == nil do
|
||||
lastArchetype += 1
|
||||
archetype = compatible_archetypes[lastArchetype]
|
||||
if not archetype then
|
||||
return nil
|
||||
end
|
||||
|
||||
entities = archetype.entities
|
||||
i = #entities
|
||||
entityId = entities[i]
|
||||
columns = archetype.columns
|
||||
local records = archetype.records
|
||||
a = columns[records[A].column]
|
||||
b = columns[records[B].column]
|
||||
end
|
||||
|
||||
local row = i
|
||||
i -= 1
|
||||
|
||||
return entityId, a[row], b[row]
|
||||
end
|
||||
elseif not D then
|
||||
function world_query_iter_next(): any
|
||||
local entityId = entities[i]
|
||||
while entityId == nil do
|
||||
lastArchetype += 1
|
||||
archetype = compatible_archetypes[lastArchetype]
|
||||
if not archetype then
|
||||
return nil
|
||||
end
|
||||
|
||||
entities = archetype.entities
|
||||
i = #entities
|
||||
entityId = entities[i]
|
||||
columns = archetype.columns
|
||||
local records = archetype.records
|
||||
a = columns[records[A].column]
|
||||
b = columns[records[B].column]
|
||||
c = columns[records[C].column]
|
||||
end
|
||||
|
||||
local row = i
|
||||
i -= 1
|
||||
|
||||
return entityId, a[row], b[row], c[row]
|
||||
end
|
||||
elseif not E then
|
||||
function world_query_iter_next(): any
|
||||
local entityId = entities[i]
|
||||
while entityId == nil do
|
||||
lastArchetype += 1
|
||||
archetype = compatible_archetypes[lastArchetype]
|
||||
if not archetype then
|
||||
return nil
|
||||
end
|
||||
|
||||
entities = archetype.entities
|
||||
i = #entities
|
||||
entityId = entities[i]
|
||||
columns = archetype.columns
|
||||
local records = archetype.records
|
||||
a = columns[records[A].column]
|
||||
b = columns[records[B].column]
|
||||
c = columns[records[C].column]
|
||||
d = columns[records[D].column]
|
||||
end
|
||||
|
||||
local row = i
|
||||
i -= 1
|
||||
|
||||
return entityId, a[row], b[row], c[row], d[row]
|
||||
end
|
||||
else
|
||||
local queryOutput = {}
|
||||
function world_query_iter_next(): any
|
||||
local entityId = entities[i]
|
||||
while entityId == nil do
|
||||
lastArchetype += 1
|
||||
archetype = compatible_archetypes[lastArchetype]
|
||||
if not archetype then
|
||||
return nil
|
||||
end
|
||||
|
||||
entities = archetype.entities
|
||||
i = #entities
|
||||
entityId = entities[i]
|
||||
columns = archetype.columns
|
||||
local records = archetype.records
|
||||
|
||||
if not F then
|
||||
a = columns[records[A].column]
|
||||
b = columns[records[B].column]
|
||||
c = columns[records[C].column]
|
||||
d = columns[records[D].column]
|
||||
e = columns[records[E].column]
|
||||
elseif not G then
|
||||
a = columns[records[A].column]
|
||||
b = columns[records[B].column]
|
||||
c = columns[records[C].column]
|
||||
d = columns[records[D].column]
|
||||
e = columns[records[E].column]
|
||||
f = columns[records[F].column]
|
||||
elseif not H then
|
||||
a = columns[records[A].column]
|
||||
b = columns[records[B].column]
|
||||
c = columns[records[C].column]
|
||||
d = columns[records[D].column]
|
||||
e = columns[records[E].column]
|
||||
f = columns[records[F].column]
|
||||
g = columns[records[G].column]
|
||||
elseif not I then
|
||||
a = columns[records[A].column]
|
||||
b = columns[records[B].column]
|
||||
c = columns[records[C].column]
|
||||
d = columns[records[D].column]
|
||||
e = columns[records[E].column]
|
||||
f = columns[records[F].column]
|
||||
g = columns[records[G].column]
|
||||
h = columns[records[H].column]
|
||||
end
|
||||
end
|
||||
|
||||
local row = i
|
||||
i -= 1
|
||||
|
||||
if not F then
|
||||
return entityId, a[row], b[row], c[row], d[row], e[row]
|
||||
elseif not G then
|
||||
return entityId, a[row], b[row], c[row], d[row], e[row], f[row]
|
||||
elseif not H then
|
||||
return entityId, a[row], b[row], c[row], d[row], e[row], f[row], g[row]
|
||||
elseif not I then
|
||||
return entityId, a[row], b[row], c[row], d[row], e[row], f[row], g[row], h[row]
|
||||
end
|
||||
|
||||
local records = archetype.records
|
||||
for j, id in ids do
|
||||
queryOutput[j] = columns[records[id].column][row]
|
||||
end
|
||||
|
||||
return entityId, unpack(queryOutput)
|
||||
end
|
||||
end
|
||||
|
||||
local function cached_query_iter()
|
||||
lastArchetype = 1
|
||||
archetype = compatible_archetypes[lastArchetype]
|
||||
if not archetype then
|
||||
return NOOP
|
||||
end
|
||||
entities = archetype.entities
|
||||
i = #entities
|
||||
records = archetype.records
|
||||
columns = archetype.columns
|
||||
if not B then
|
||||
a = columns[records[A].column]
|
||||
elseif not C then
|
||||
a = columns[records[A].column]
|
||||
b = columns[records[B].column]
|
||||
elseif not D then
|
||||
a = columns[records[A].column]
|
||||
b = columns[records[B].column]
|
||||
c = columns[records[C].column]
|
||||
elseif not E then
|
||||
a = columns[records[A].column]
|
||||
b = columns[records[B].column]
|
||||
c = columns[records[C].column]
|
||||
d = columns[records[D].column]
|
||||
elseif not F then
|
||||
a = columns[records[A].column]
|
||||
b = columns[records[B].column]
|
||||
c = columns[records[C].column]
|
||||
d = columns[records[D].column]
|
||||
e = columns[records[E].column]
|
||||
elseif not G then
|
||||
a = columns[records[A].column]
|
||||
b = columns[records[B].column]
|
||||
c = columns[records[C].column]
|
||||
d = columns[records[D].column]
|
||||
e = columns[records[E].column]
|
||||
f = columns[records[F].column]
|
||||
elseif not H then
|
||||
a = columns[records[A].column]
|
||||
b = columns[records[B].column]
|
||||
c = columns[records[C].column]
|
||||
d = columns[records[D].column]
|
||||
e = columns[records[E].column]
|
||||
f = columns[records[F].column]
|
||||
g = columns[records[G].column]
|
||||
elseif not I then
|
||||
a = columns[records[A].column]
|
||||
b = columns[records[B].column]
|
||||
c = columns[records[C].column]
|
||||
d = columns[records[D].column]
|
||||
e = columns[records[E].column]
|
||||
f = columns[records[F].column]
|
||||
g = columns[records[G].column]
|
||||
h = columns[records[H].column]
|
||||
end
|
||||
|
||||
return world_query_iter_next
|
||||
end
|
||||
|
||||
return setmetatable(query, {
|
||||
__index = {
|
||||
archetypes = query_archetypes,
|
||||
__iter = cached_query_iter,
|
||||
iter = cached_query_iter
|
||||
}
|
||||
})
|
||||
return query
|
||||
end
|
||||
|
||||
local Query = {}
|
||||
|
@ -2177,11 +1943,12 @@ export type Query<T...> = typeof(setmetatable({}, {
|
|||
|
||||
type QueryInner = {
|
||||
compatible_archetypes: { Archetype },
|
||||
filter_with: { i53 }?,
|
||||
filter_without: { i53 }?,
|
||||
filters: {
|
||||
without: { i53 }?,
|
||||
with: { i53 }?,
|
||||
}?,
|
||||
ids: { i53 },
|
||||
world: {}, -- Downcasted to be serializable by the analyzer
|
||||
next: () -> Item<any>
|
||||
world: {} -- Downcasted to be serializable by the analyzer
|
||||
}
|
||||
|
||||
type Observer = {
|
||||
|
|
|
@ -59,7 +59,6 @@ local function debug_world_inspect(world: World)
|
|||
records = records,
|
||||
row = row,
|
||||
tuple = tuple,
|
||||
columns = columns
|
||||
}
|
||||
end
|
||||
|
||||
|
@ -372,25 +371,10 @@ TEST("world:query()", function()
|
|||
local q = world:query(Foo, Bar):without(Baz):cached()
|
||||
world:set(e, Foo, true)
|
||||
world:set(e, Bar, false)
|
||||
local i = 0
|
||||
|
||||
for _, e in q:iter() do
|
||||
i=1
|
||||
end
|
||||
CHECK(i == 1)
|
||||
for _, e in q:iter() do
|
||||
i=2
|
||||
end
|
||||
CHECK(i == 2)
|
||||
world:set(e, Baz, true)
|
||||
for _, e in q do
|
||||
i=3
|
||||
CHECK(true)
|
||||
end
|
||||
CHECK(i == 3)
|
||||
for _, e in q do
|
||||
i=4
|
||||
end
|
||||
CHECK(i == 4)
|
||||
|
||||
CHECK(#q:archetypes() == 1)
|
||||
CHECK(not table.find(q:archetypes(), world.archetypes[table.concat({Foo, Bar, Baz}, "_")]))
|
||||
world:delete(Foo)
|
||||
|
@ -1209,38 +1193,25 @@ TEST("world:delete", function()
|
|||
end)
|
||||
|
||||
TEST("world:target", function()
|
||||
do CASE("nth index")
|
||||
do
|
||||
CASE("nth index")
|
||||
local world = world_new()
|
||||
local A = world:component()
|
||||
world:set(A, jecs.Name, "A")
|
||||
local B = world:component()
|
||||
world:set(B, jecs.Name, "B")
|
||||
local C = world:component()
|
||||
world:set(C, jecs.Name, "C")
|
||||
local D = world:component()
|
||||
world:set(D, jecs.Name, "D")
|
||||
local E = world:component()
|
||||
world:set(E, jecs.Name, "E")
|
||||
local e = world:entity()
|
||||
|
||||
world:add(e, pair(A, B))
|
||||
world:add(e, pair(A, C))
|
||||
world:add(e, pair(A, D))
|
||||
world:add(e, pair(A, E))
|
||||
world:add(e, pair(B, C))
|
||||
world:add(e, pair(B, D))
|
||||
world:add(e, pair(C, D))
|
||||
|
||||
CHECK(pair(A, B) < pair(A, C))
|
||||
CHECK(pair(A, E) < pair(B, C))
|
||||
|
||||
local records = debug_world_inspect(world).records(e)
|
||||
CHECK(jecs.pair_first(world, pair(B, C)) == B)
|
||||
CHECK(records[pair(B, C)].column > records[pair(A, E)].column)
|
||||
CHECK(world:target(e, A, 0) == B)
|
||||
CHECK(world:target(e, A, 1) == C)
|
||||
CHECK(world:target(e, A, 2) == D)
|
||||
CHECK(world:target(e, A, 3) == E)
|
||||
CHECK(world:target(e, B, 0) == C)
|
||||
CHECK(world:target(e, B, 1) == D)
|
||||
CHECK(world:target(e, C, 0) == D)
|
||||
|
|
Loading…
Reference in a new issue