mirror of
https://github.com/Ukendio/jecs.git
synced 2025-04-25 01:20:04 +00:00
Make EcsWildcard a component
This commit is contained in:
parent
8e0a9409f5
commit
8d1a62b71c
2 changed files with 255 additions and 265 deletions
|
@ -57,7 +57,7 @@ local function flip()
|
|||
end
|
||||
|
||||
local common = 0
|
||||
local N = 2^16-2
|
||||
local N = 5000
|
||||
local archetypes = {}
|
||||
|
||||
local hm = 0
|
||||
|
@ -170,13 +170,8 @@ return {
|
|||
end,
|
||||
|
||||
Functions = {
|
||||
Matter = function()
|
||||
for entityId, firstComponent in newWorld:query(A1, A2, A3, A4) do
|
||||
end
|
||||
end,
|
||||
|
||||
ECR = function()
|
||||
for entityId, firstComponent in registry2:view(B1, B2, B3, B3) do
|
||||
Mirror = function()
|
||||
for entityId, firstComponent in mcs:query(E1, E2, E3, E4) do
|
||||
end
|
||||
end,
|
||||
|
||||
|
|
509
src/init.luau
509
src/init.luau
|
@ -942,73 +942,70 @@ local EmptyQuery = {
|
|||
|
||||
setmetatable(EmptyQuery, EmptyQuery)
|
||||
|
||||
local function columns_replace_values(row, columns, ...)
|
||||
for i, column in columns do
|
||||
column[row] = select(i, ...)
|
||||
end
|
||||
end
|
||||
|
||||
local function world_query(world: World, ...)
|
||||
local compatible_archetypes = {}
|
||||
local length = 0
|
||||
|
||||
local ids = { ... }
|
||||
local A, B, C, D, E, F, G, H, I = ...
|
||||
local a, b, c, d, e, f, g, h
|
||||
|
||||
local archetypes = world.archetypes
|
||||
|
||||
local idr: ArchetypeMap
|
||||
local componentIndex = world.componentIndex
|
||||
|
||||
for _, id in ids do
|
||||
local map = componentIndex[id]
|
||||
if not map then
|
||||
return EmptyQuery
|
||||
end
|
||||
|
||||
if idr == nil or map.size < idr.size then
|
||||
idr = map
|
||||
end
|
||||
end
|
||||
|
||||
for archetype_id in idr.cache do
|
||||
local compatibleArchetype = archetypes[archetype_id]
|
||||
if #compatibleArchetype.entities == 0 then
|
||||
continue
|
||||
end
|
||||
local records = compatibleArchetype.records
|
||||
|
||||
local skip = false
|
||||
|
||||
for i, id in ids do
|
||||
local tr = records[id]
|
||||
if not tr then
|
||||
skip = true
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if skip then
|
||||
continue
|
||||
end
|
||||
|
||||
length += 1
|
||||
compatible_archetypes[length] = compatibleArchetype
|
||||
end
|
||||
|
||||
if length == 0 then
|
||||
local function query_init(query)
|
||||
local world_query_iter_next = query.iter_next
|
||||
if world_query_iter_next then
|
||||
return world_query_iter_next
|
||||
end
|
||||
local compatible_archetypes = query.compatible_archetypes
|
||||
local ids = query.ids
|
||||
local A, B, C, D, E, F, G, H, I = unpack(ids)
|
||||
local a, b, c, d, e, f, g, h
|
||||
local lastArchetype = 1
|
||||
local archetype = compatible_archetypes[1]
|
||||
if not archetype then
|
||||
return EmptyQuery
|
||||
end
|
||||
local columns = archetype.columns
|
||||
local entities = archetype.entities
|
||||
local i = #entities
|
||||
|
||||
local lastArchetype = 1
|
||||
local archetype
|
||||
local columns
|
||||
local entities
|
||||
local i
|
||||
local queryOutput
|
||||
|
||||
local world_query_iter_next
|
||||
local records = archetype.records
|
||||
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
|
||||
|
||||
if not B then
|
||||
function world_query_iter_next(): any
|
||||
|
@ -1121,6 +1118,7 @@ local function world_query(world: World, ...)
|
|||
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
|
||||
|
@ -1194,221 +1192,217 @@ local function world_query(world: World, ...)
|
|||
end
|
||||
end
|
||||
|
||||
local init = false
|
||||
local drain = false
|
||||
query.iter_next = world_query_iter_next
|
||||
return world_query_iter_next
|
||||
end
|
||||
|
||||
local function query_init(query)
|
||||
if init and drain then
|
||||
return true
|
||||
end
|
||||
local function query_iter(query)
|
||||
return query_init(query)
|
||||
end
|
||||
|
||||
init = true
|
||||
lastArchetype = 1
|
||||
archetype = compatible_archetypes[lastArchetype]
|
||||
local function query_drain(query)
|
||||
local query_iter_next = query_init(query)
|
||||
query.next = query_iter_next
|
||||
return query
|
||||
end
|
||||
|
||||
if not archetype then
|
||||
return false
|
||||
end
|
||||
|
||||
queryOutput = {}
|
||||
|
||||
entities = archetype.entities
|
||||
i = #entities
|
||||
columns = archetype.columns
|
||||
local function query_next(query)
|
||||
error("Did you forget to call drain?")
|
||||
end
|
||||
|
||||
local function query_without(query, ...)
|
||||
local compatible_archetypes = query.compatible_archetypes
|
||||
local length: number = query.length
|
||||
local N = select("#", ...)
|
||||
for i = #compatible_archetypes, 1, -1 do
|
||||
local archetype = compatible_archetypes[i]
|
||||
local records = archetype.records
|
||||
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 true
|
||||
end
|
||||
local shouldRemove = false
|
||||
|
||||
local function world_query_without(query, ...)
|
||||
local N = select("#", ...)
|
||||
for i = #compatible_archetypes, 1, -1 do
|
||||
local archetype = compatible_archetypes[i]
|
||||
local records = archetype.records
|
||||
local shouldRemove = false
|
||||
|
||||
for j = 1, N do
|
||||
local id = select(j, ...)
|
||||
if records[id] then
|
||||
shouldRemove = true
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if shouldRemove then
|
||||
local last = #compatible_archetypes
|
||||
if last ~= i then
|
||||
compatible_archetypes[i] = compatible_archetypes[last]
|
||||
end
|
||||
compatible_archetypes[last] = nil
|
||||
length -= 1
|
||||
for j = 1, N do
|
||||
local id = select(j, ...)
|
||||
if records[id] then
|
||||
shouldRemove = true
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if length == 0 then
|
||||
return EmptyQuery
|
||||
end
|
||||
|
||||
return query
|
||||
end
|
||||
|
||||
local function world_query_replace(query, fn: (...any) -> ...any)
|
||||
query_init(query)
|
||||
|
||||
for i, archetype in compatible_archetypes do
|
||||
local columns = archetype.columns
|
||||
local records = archetype.records
|
||||
for row in archetype.entities do
|
||||
if not B then
|
||||
local va = columns[records[A].column]
|
||||
local pa = fn(va[row])
|
||||
|
||||
va[row] = pa
|
||||
elseif not C then
|
||||
local va = columns[records[A].column]
|
||||
local vb = columns[records[B].column]
|
||||
|
||||
va[row], vb[row] = fn(va[row], vb[row])
|
||||
elseif not D then
|
||||
local va = columns[records[A].column]
|
||||
local vb = columns[records[B].column]
|
||||
local vc = columns[records[C].column]
|
||||
|
||||
va[row], vb[row], vc[row] = fn(va[row], vb[row], vc[row])
|
||||
elseif not E then
|
||||
local va = columns[records[A].column]
|
||||
local vb = columns[records[B].column]
|
||||
local vc = columns[records[C].column]
|
||||
local vd = columns[records[D].column]
|
||||
|
||||
va[row], vb[row], vc[row], vd[row] = fn(va[row], vb[row], vc[row], vd[row])
|
||||
else
|
||||
for j, id in ids do
|
||||
local tr = records[id]
|
||||
queryOutput[j] = columns[tr.column][row]
|
||||
end
|
||||
columns_replace_values(row, columns, fn(unpack(queryOutput)))
|
||||
end
|
||||
if shouldRemove then
|
||||
local last = #compatible_archetypes
|
||||
if last ~= i then
|
||||
compatible_archetypes[i] = compatible_archetypes[last]
|
||||
end
|
||||
compatible_archetypes[last] = nil
|
||||
length -= 1
|
||||
end
|
||||
end
|
||||
|
||||
local function world_query_with(query, ...)
|
||||
local N = select("#", ...)
|
||||
for i = #compatible_archetypes, 1, -1 do
|
||||
local archetype = compatible_archetypes[i]
|
||||
local records = archetype.records
|
||||
local shouldRemove = false
|
||||
query.length = length
|
||||
|
||||
for j = 1, N do
|
||||
local id = select(j, ...)
|
||||
if not records[id] then
|
||||
shouldRemove = true
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if shouldRemove then
|
||||
local last = #compatible_archetypes
|
||||
if last ~= i then
|
||||
compatible_archetypes[i] = compatible_archetypes[last]
|
||||
end
|
||||
compatible_archetypes[last] = nil
|
||||
length -= 1
|
||||
end
|
||||
end
|
||||
if length == 0 then
|
||||
return EmptyQuery
|
||||
end
|
||||
return query
|
||||
end
|
||||
|
||||
-- Meant for directly iterating over archetypes to minimize
|
||||
-- function call overhead. Should not be used unless iterating over
|
||||
-- hundreds of thousands of entities in bulk.
|
||||
local function world_query_archetypes()
|
||||
return compatible_archetypes
|
||||
end
|
||||
|
||||
local function world_query_drain(query)
|
||||
drain = true
|
||||
if query_init(query) then
|
||||
return query
|
||||
end
|
||||
if length == 0 then
|
||||
return EmptyQuery
|
||||
end
|
||||
|
||||
local function world_query_iter(query)
|
||||
query_init(query)
|
||||
return world_query_iter_next
|
||||
end
|
||||
return query
|
||||
end
|
||||
|
||||
local function world_query_next(world)
|
||||
if not drain then
|
||||
error("Did you forget to call query:drain()?")
|
||||
local function query_with(query, ...)
|
||||
local compatible_archetypes = query.compatible_archetypes
|
||||
local length: number = query.length
|
||||
local N = select("#", ...)
|
||||
for i = #compatible_archetypes, 1, -1 do
|
||||
local archetype = compatible_archetypes[i]
|
||||
local records = archetype.records
|
||||
local shouldRemove = false
|
||||
|
||||
for j = 1, N do
|
||||
local id = select(j, ...)
|
||||
if not records[id] then
|
||||
shouldRemove = true
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if shouldRemove then
|
||||
local last = #compatible_archetypes
|
||||
if last ~= i then
|
||||
compatible_archetypes[i] = compatible_archetypes[last]
|
||||
end
|
||||
compatible_archetypes[last] = nil
|
||||
length -= 1
|
||||
end
|
||||
end
|
||||
query.length = length
|
||||
if length == 0 then
|
||||
return EmptyQuery
|
||||
end
|
||||
return query
|
||||
end
|
||||
|
||||
local function columns_replace_values(row, columns, ...)
|
||||
for i, column in columns do
|
||||
column[row] = select(i, ...)
|
||||
end
|
||||
end
|
||||
|
||||
local function query_replace(query, fn: (...any) -> ...any)
|
||||
local compatible_archetypes = query.compatible_archetypes
|
||||
local ids = query.ids
|
||||
local A, B, C, D, E = unpack(ids, 1, 5)
|
||||
local queryOutput = {}
|
||||
for i, archetype in compatible_archetypes do
|
||||
local columns = archetype.columns
|
||||
local records = archetype.records
|
||||
for row in archetype.entities do
|
||||
if not B then
|
||||
local va = columns[records[A].column]
|
||||
local pa = fn(va[row])
|
||||
|
||||
va[row] = pa
|
||||
elseif not C then
|
||||
local va = columns[records[A].column]
|
||||
local vb = columns[records[B].column]
|
||||
|
||||
va[row], vb[row] = fn(va[row], vb[row])
|
||||
elseif not D then
|
||||
local va = columns[records[A].column]
|
||||
local vb = columns[records[B].column]
|
||||
local vc = columns[records[C].column]
|
||||
|
||||
va[row], vb[row], vc[row] = fn(va[row], vb[row], vc[row])
|
||||
elseif not E then
|
||||
local va = columns[records[A].column]
|
||||
local vb = columns[records[B].column]
|
||||
local vc = columns[records[C].column]
|
||||
local vd = columns[records[D].column]
|
||||
|
||||
va[row], vb[row], vc[row], vd[row] = fn(va[row], vb[row], vc[row], vd[row])
|
||||
else
|
||||
for j, id in ids do
|
||||
local tr = records[id]
|
||||
queryOutput[j] = columns[tr.column][row]
|
||||
end
|
||||
columns_replace_values(row, columns, fn(unpack(queryOutput)))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Meant for directly iterating over archetypes to minimize
|
||||
-- function call overhead. Should not be used unless iterating over
|
||||
-- hundreds of thousands of entities in bulk.
|
||||
local function query_archetypes(query)
|
||||
return query.compatible_archetypes
|
||||
end
|
||||
|
||||
local Query = {}
|
||||
Query.__index = Query
|
||||
Query.__iter = query_iter
|
||||
Query.without = query_without
|
||||
Query.with = query_with
|
||||
Query.archetypes = query_archetypes
|
||||
Query.drain = query_drain
|
||||
Query.next = query_next
|
||||
Query.replace = query_replace
|
||||
|
||||
local function world_query(world: World, ...)
|
||||
local compatible_archetypes = {}
|
||||
local length = 0
|
||||
|
||||
local ids = { ... }
|
||||
|
||||
local archetypes = world.archetypes
|
||||
|
||||
local idr: ArchetypeMap
|
||||
local componentIndex = world.componentIndex
|
||||
|
||||
for _, id in ids do
|
||||
local map = componentIndex[id]
|
||||
if not map then
|
||||
return EmptyQuery
|
||||
end
|
||||
|
||||
if idr == nil or map.size < idr.size then
|
||||
idr = map
|
||||
end
|
||||
return world_query_iter_next(world)
|
||||
end
|
||||
|
||||
local it = {
|
||||
__iter = world_query_iter,
|
||||
iter = world_query_iter,
|
||||
drain = world_query_drain,
|
||||
next = world_query_next,
|
||||
with = world_query_with,
|
||||
without = world_query_without,
|
||||
replace = world_query_replace,
|
||||
archetypes = world_query_archetypes,
|
||||
} :: any
|
||||
for archetype_id in idr.cache do
|
||||
local compatibleArchetype = archetypes[archetype_id]
|
||||
if #compatibleArchetype.entities == 0 then
|
||||
continue
|
||||
end
|
||||
local records = compatibleArchetype.records
|
||||
|
||||
setmetatable(it, it)
|
||||
local skip = false
|
||||
|
||||
return it
|
||||
for i, id in ids do
|
||||
local tr = records[id]
|
||||
if not tr then
|
||||
skip = true
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if skip then
|
||||
continue
|
||||
end
|
||||
|
||||
length += 1
|
||||
compatible_archetypes[length] = compatibleArchetype
|
||||
end
|
||||
|
||||
if length == 0 then
|
||||
return EmptyQuery
|
||||
end
|
||||
|
||||
local q = setmetatable({
|
||||
compatible_archetypes = compatible_archetypes,
|
||||
length = length,
|
||||
ids = ids,
|
||||
}, Query)
|
||||
|
||||
return q
|
||||
end
|
||||
|
||||
local World = {}
|
||||
|
@ -1519,6 +1513,7 @@ function World.new()
|
|||
end
|
||||
|
||||
world_add(self, EcsChildOf, ECS_PAIR(EcsOnDeleteTarget, EcsDelete))
|
||||
world_add(self, EcsWildcard, EcsComponent)
|
||||
|
||||
return self
|
||||
end
|
||||
|
@ -1655,7 +1650,7 @@ return {
|
|||
Name = EcsName :: Entity<string>,
|
||||
Rest = EcsRest :: Entity,
|
||||
|
||||
pair = (ECS_PAIR :: any) :: <R, T>(pred: Entity, obj: Entity) -> number,
|
||||
pair = ECS_PAIR,
|
||||
|
||||
-- Inwards facing API for testing
|
||||
ECS_ID = ECS_ENTITY_T_LO,
|
||||
|
|
Loading…
Reference in a new issue