Merge branch 'main' of https://github.com/Ukendio/jecs into example

This commit is contained in:
Ukendio 2024-08-03 05:46:25 +02:00
commit 3ba6a2fb49

View file

@ -714,16 +714,16 @@ export type Query = typeof({
type CompatibleArchetype = { archetype: Archetype, indices: { number } } type CompatibleArchetype = { archetype: Archetype, indices: { number } }
local world_query: (World, ...i53) -> Query local noop: Item = function()
return nil :: any
end
local Arm = function(self: Query, ...)
return self
end
local world_query
do do
local noop: Item = function()
return nil :: any
end
local Arm = function(self: Query, ...)
return self
end
local EmptyQuery: Query = { local EmptyQuery: Query = {
__iter = function(): Item __iter = function(): Item
return noop return noop
@ -737,260 +737,245 @@ do
setmetatable(EmptyQuery, EmptyQuery) setmetatable(EmptyQuery, EmptyQuery)
local lastArchetype: number local function world_query_replace_values(row, columns, ...)
local archetype: Archetype for i, column in columns do
local queryOutput: { any } column[row] = select(i, ...)
local entities: { number }
local i: number
local compatible_archetypes: { Archetype }
local ids: { number }
local columns
local A, B, C, D, E, F, G, H, I
local a, b, c, d, e, f, g, h
local init
local drain
local function query_init(query)
if init and drain then
return
end
init = true
lastArchetype = 1
archetype = compatible_archetypes[lastArchetype]
if not archetype then
return
end end
queryOutput = {}
entities = archetype.entities
i = #entities
columns = archetype.columns
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 H then
a = columns[records[A].column]
b = columns[records[B].column]
c = 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 end
local world_query_iter_next function world_query(world, ...)
-- breaking
if (...) == nil then
error("Missing components")
end
local function world_query_iter_create() local compatible_archetypes = {}
if not B then local length = 0
function world_query_iter_next(): any
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]
local tr = compatibleArchetype.records
local skip = false
for i, id in ids do
local index = tr[id]
if not index 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 lastArchetype = 1
local archetype
local columns
local entities
local i
local queryOutput
local world_query_iter_next
if not B then
function world_query_iter_next(): any
local entityId = entities[i] local entityId = entities[i]
while entityId == nil do while entityId == nil do
lastArchetype += 1 lastArchetype += 1
archetype = compatible_archetypes[lastArchetype] archetype = compatible_archetypes[lastArchetype]
if not archetype then if not archetype then
return nil return nil
end end
entities = archetype.entities entities = archetype.entities
i = #entities i = #entities
if i == 0 then if i == 0 then
continue continue
end end
entityId = entities[i] entityId = entities[i]
columns = archetype.columns columns = archetype.columns
local records = archetype.records local records = archetype.records
a = columns[records[A].column] a = columns[records[A].column]
end end
local row = i local row = i
i-=1 i-=1
return entityId, a[row] return entityId, a[row]
end end
elseif not C then elseif not C then
function world_query_iter_next(): any function world_query_iter_next(): any
local entityId = entities[i] local entityId = entities[i]
while entityId == nil do while entityId == nil do
lastArchetype += 1 lastArchetype += 1
archetype = compatible_archetypes[lastArchetype] archetype = compatible_archetypes[lastArchetype]
if not archetype then if not archetype then
return nil return nil
end end
entities = archetype.entities entities = archetype.entities
i = #entities i = #entities
if i == 0 then if i == 0 then
continue continue
end end
entityId = entities[i] entityId = entities[i]
columns = archetype.columns columns = archetype.columns
local records = archetype.records local records = archetype.records
a = columns[records[A].column] a = columns[records[A].column]
b = columns[records[B].column] b = columns[records[B].column]
end end
local row = i local row = i
i-=1 i-=1
return entityId, a[row], b[row] return entityId, a[row], b[row]
end end
elseif not D then elseif not D then
function world_query_iter_next(): any function world_query_iter_next(): any
local entityId = entities[i] local entityId = entities[i]
while entityId == nil do while entityId == nil do
lastArchetype += 1 lastArchetype += 1
archetype = compatible_archetypes[lastArchetype] archetype = compatible_archetypes[lastArchetype]
if not archetype then if not archetype then
return nil return nil
end end
entities = archetype.entities entities = archetype.entities
i = #entities i = #entities
if i == 0 then if i == 0 then
continue continue
end end
entityId = entities[i] entityId = entities[i]
columns = archetype.columns columns = archetype.columns
local records = archetype.records local records = archetype.records
a = columns[records[A].column] a = columns[records[A].column]
b = columns[records[B].column] b = columns[records[B].column]
c = columns[records[C].column] c = columns[records[C].column]
end end
local row = i local row = i
i-=1 i-=1
return entityId, a[row], b[row], c[row] return entityId, a[row], b[row], c[row]
end end
elseif not E then elseif not E then
function world_query_iter_next(): any function world_query_iter_next(): any
local entityId = entities[i] local entityId = entities[i]
while entityId == nil do while entityId == nil do
lastArchetype += 1 lastArchetype += 1
archetype = compatible_archetypes[lastArchetype] archetype = compatible_archetypes[lastArchetype]
if not archetype then if not archetype then
return nil return nil
end end
entities = archetype.entities entities = archetype.entities
i = #entities i = #entities
if i == 0 then if i == 0 then
continue continue
end end
entityId = entities[i] entityId = entities[i]
columns = archetype.columns columns = archetype.columns
local records = archetype.records local records = archetype.records
a = columns[records[A].column] a = columns[records[A].column]
b = columns[records[B].column] b = columns[records[B].column]
c = columns[records[C].column] c = columns[records[C].column]
d = columns[records[D].column] d = columns[records[D].column]
end end
local row = i local row = i
i-=1 i-=1
return entityId, a[row], b[row], c[row], d[row] return entityId, a[row], b[row], c[row], d[row]
end end
else else
function world_query_iter_next(): any function world_query_iter_next(): any
local entityId = entities[i] local entityId = entities[i]
while entityId == nil do while entityId == nil do
lastArchetype += 1 lastArchetype += 1
archetype = compatible_archetypes[lastArchetype] archetype = compatible_archetypes[lastArchetype]
if not archetype then if not archetype then
return nil return nil
end end
entities = archetype.entities entities = archetype.entities
i = #entities i = #entities
if i == 0 then if i == 0 then
continue continue
end end
entityId = entities[i] entityId = entities[i]
columns = archetype.columns columns = archetype.columns
local records = archetype.records local records = archetype.records
if not F then if not F then
a = columns[records[A].column] a = columns[records[A].column]
b = columns[records[B].column] b = columns[records[B].column]
c = columns[records[C].column] c = columns[records[C].column]
d = columns[records[D].column] d = columns[records[D].column]
e = columns[records[E].column] e = columns[records[E].column]
elseif not G then elseif not G then
a = columns[records[A].column] a = columns[records[A].column]
b = columns[records[B].column] b = columns[records[B].column]
c = columns[records[C].column] c = columns[records[C].column]
d = columns[records[D].column] d = columns[records[D].column]
e = columns[records[E].column] e = columns[records[E].column]
f = columns[records[F].column] f = columns[records[F].column]
elseif not H then elseif not H then
a = columns[records[A].column] a = columns[records[A].column]
b = columns[records[B].column] b = columns[records[B].column]
c = columns[records[C].column] c = columns[records[C].column]
d = columns[records[D].column] d = columns[records[D].column]
e = columns[records[E].column] e = columns[records[E].column]
f = columns[records[F].column] f = columns[records[F].column]
g = columns[records[G].column] g = columns[records[G].column]
elseif not I then elseif not I then
a = columns[records[A].column] a = columns[records[A].column]
b = columns[records[B].column] b = columns[records[B].column]
c = columns[records[C].column] c = columns[records[C].column]
d = columns[records[D].column] d = columns[records[D].column]
e = columns[records[E].column] e = columns[records[E].column]
f = columns[records[F].column] f = columns[records[F].column]
g = columns[records[G].column] g = columns[records[G].column]
h = columns[records[H].column] h = columns[records[H].column]
end end
end end
local row = i local row = i
i-=1 i-=1
if not F then if not F then
return entityId, a[row], b[row], c[row], d[row], e[row] return entityId, a[row], b[row], c[row], d[row], e[row]
elseif not G then elseif not G then
return entityId, a[row], b[row], c[row], d[row], e[row], f[row] return entityId, a[row], b[row], c[row], d[row], e[row], f[row]
elseif not H then elseif not H then
return entityId, a[row], b[row], c[row], d[row], e[row], f[row], g[row] return entityId, a[row], b[row], c[row], d[row], e[row], f[row], g[row]
elseif not I then elseif not I then
@ -999,218 +984,233 @@ do
local field = archetype.records local field = archetype.records
for j, id in ids do for j, id in ids do
queryOutput[j] = columns[field[id].column][row] queryOutput[j] = columns[field[id].column][row]
end end
return entityId, unpack(queryOutput) return entityId, unpack(queryOutput)
end end
end
end
local function world_query_without(self, ...)
local withoutComponents = { ... }
for i = #compatible_archetypes, 1, -1 do
local archetype = compatible_archetypes[i]
local records = archetype.records
local shouldRemove = false
for _, componentId in withoutComponents do
if records[componentId] 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
end
end
return self
end
local function world_query_replace_values(row, columns, ...)
for i, column in columns do
column[row] = select(i, ...)
end
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 tr = archetype.records
for row in archetype.entities do
if not B then
local va = columns[tr[A].column]
local pa = fn(va[row])
va[row] = pa
elseif not C then
local va = columns[tr[A].column]
local vb = columns[tr[B].column]
va[row], vb[row] = fn(va[row], vb[row])
elseif not D then
local va = columns[tr[A].column]
local vb = columns[tr[B].column]
local vc = columns[tr[C].column]
va[row], vb[row], vc[row] = fn(va[row], vb[row], vc[row])
elseif not E then
local va = columns[tr[A].column]
local vb = columns[tr[B].column]
local vc = columns[tr[C].column]
local vd = columns[tr[D].column]
va[row], vb[row], vc[row], vd[row] = fn(
va[row], vb[row], vc[row], vd[row])
else
local field = archetype.records
for j, id in ids do
queryOutput[j] = columns[field[id].column][row]
end
world_query_replace_values(row, columns,
fn(unpack(queryOutput)))
end
end
end
end
local function world_query_with(query, ...)
local ids = { ... }
for i = #compatible_archetypes, 1, -1 do
local archetype = compatible_archetypes[i]
local records = archetype.records
local shouldRemove = false
for _, id in ids do
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
end
end
query_init(query)
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
query_init(query)
return query
end
local function world_query_iter(query)
query_init(query)
return world_query_iter_next
end
local function world_query_next()
if not drain then
error("Did you forget to call query:drain()?")
end end
return world_query_iter_next()
end
local it = { local init = false
__iter = world_query_iter, local drain = false
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
setmetatable(it, it) local function query_init(query)
if init and drain then
return
end
function world_query(world: World, ...: any): Query init = true
-- breaking lastArchetype = 1
if (...) == nil then archetype = compatible_archetypes[lastArchetype]
error("Missing components")
end
compatible_archetypes = {} if not archetype then
local length = 0 return false
end
local components = { ... } :: any queryOutput = {}
A, B, C, D, E, F, G, H, I = ...
local archetypes = world.archetypes entities = archetype.entities
i = #entities
columns = archetype.columns
local firstArchetypeMap: ArchetypeMap local records = archetype.records
local componentIndex = world.componentIndex 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
for _, componentId in components do local function world_query_without(query, ...)
local map = componentIndex[componentId] local withoutComponents = { ... }
if not map then for i = #compatible_archetypes, 1, -1 do
return EmptyQuery local archetype = compatible_archetypes[i]
end local records = archetype.records
local shouldRemove = false
if firstArchetypeMap == nil or map.size < firstArchetypeMap.size then for _, componentId in withoutComponents do
firstArchetypeMap = map if records[componentId] then
end shouldRemove = true
end break
end
end
for id in firstArchetypeMap.cache do if shouldRemove then
local compatibleArchetype = archetypes[id] local last = #compatible_archetypes
local archetypeRecords = compatibleArchetype.records if last ~= i then
compatible_archetypes[i] = compatible_archetypes[last]
end
compatible_archetypes[last] = nil
length -= 1
end
end
local skip = false if length == 0 then
return EmptyQuery
end
for i, componentId in components do return query
local index = archetypeRecords[componentId] end
if not index then
skip = true
break
end
end
if skip then local function world_query_replace(query, fn: (...any) -> (...any))
continue query_init(query)
end
length += 1 for i, archetype in compatible_archetypes do
compatible_archetypes[length] = compatibleArchetype local columns = archetype.columns
end local tr = archetype.records
for row in archetype.entities do
if not B then
local va = columns[tr[A].column]
local pa = fn(va[row])
drain = false va[row] = pa
init = false elseif not C then
ids = components local va = columns[tr[A].column]
local vb = columns[tr[B].column]
world_query_iter_create() va[row], vb[row] = fn(va[row], vb[row])
elseif not D then
local va = columns[tr[A].column]
local vb = columns[tr[B].column]
local vc = columns[tr[C].column]
return it va[row], vb[row], vc[row] = fn(va[row], vb[row], vc[row])
elseif not E then
local va = columns[tr[A].column]
local vb = columns[tr[B].column]
local vc = columns[tr[C].column]
local vd = columns[tr[D].column]
va[row], vb[row], vc[row], vd[row] = fn(
va[row], vb[row], vc[row], vd[row])
else
local field = archetype.records
for j, id in ids do
queryOutput[j] = columns[field[id].column][row]
end
world_query_replace_values(row, columns,
fn(unpack(queryOutput)))
end
end
end
end
local function world_query_with(query, ...)
local with = { ... }
for i = #compatible_archetypes, 1, -1 do
local archetype = compatible_archetypes[i]
local tr = archetype.records
local shouldRemove = false
for _, id in with do
if not tr[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
return EmptyQuery
end
local function world_query_iter(query)
query_init(query)
return world_query_iter_next
end
local function world_query_next(world)
if not drain then
error("Did you forget to call query:drain()?")
end
return world_query_iter_next(world)
end
local it = {
__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
setmetatable(it, it)
return it
end end
end end
type WorldIterator = (() -> (i53, { [unknown]: unknown? })) & (() -> ()) & (() -> i53)
-- __nominal_type_dont_use could not be any or T as it causes a type error -- __nominal_type_dont_use could not be any or T as it causes a type error
-- or produces a union -- or produces a union
export type Entity<T = any> = number & { __nominal_type_dont_use: T } export type Entity<T = any> = number & { __DO_NOT_USE_OR_YOU_WILL_BE_FIRED: T }
export type Pair = number export type Pair = number
export type QueryShim<T...> = typeof(setmetatable({ export type QueryShim<T...> = typeof(setmetatable({