mirror of
https://github.com/Ukendio/jecs.git
synced 2025-04-24 17:10:03 +00:00
Index indices after archetype matched
This commit is contained in:
parent
14507ac775
commit
14d53e03c1
4 changed files with 298 additions and 160 deletions
|
@ -8,7 +8,7 @@ local function TITLE(title: string)
|
||||||
print(testkit.color.white(title))
|
print(testkit.color.white(title))
|
||||||
end
|
end
|
||||||
|
|
||||||
local jecs = require("../lib/init")
|
local jecs = require("../mirror/init")
|
||||||
local ecs = jecs.World.new()
|
local ecs = jecs.World.new()
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -170,9 +170,9 @@ return {
|
||||||
end,
|
end,
|
||||||
|
|
||||||
Functions = {
|
Functions = {
|
||||||
Matter = function()
|
Mirror = function()
|
||||||
local matched = 0
|
local matched = 0
|
||||||
for entityId, firstComponent in newWorld:query(A1, A4, A6, A8) do
|
for entityId, firstComponent in mcs:query(E1, E4, E6, E8) do
|
||||||
matched += 1
|
matched += 1
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
|
|
14
lib/init.lua
14
lib/init.lua
|
@ -406,11 +406,7 @@ function World.query(world: World, ...: i53): Query
|
||||||
if skip then
|
if skip then
|
||||||
continue
|
continue
|
||||||
end
|
end
|
||||||
|
table.insert(compatibleArchetypes, { archetype, indices })
|
||||||
table.insert(compatibleArchetypes, {
|
|
||||||
archetype = archetype,
|
|
||||||
indices = indices
|
|
||||||
})
|
|
||||||
end
|
end
|
||||||
|
|
||||||
local lastArchetype, compatibleArchetype = next(compatibleArchetypes)
|
local lastArchetype, compatibleArchetype = next(compatibleArchetypes)
|
||||||
|
@ -424,7 +420,7 @@ function World.query(world: World, ...: i53): Query
|
||||||
function preparedQuery:without(...)
|
function preparedQuery:without(...)
|
||||||
local components = { ... }
|
local components = { ... }
|
||||||
for i = #compatibleArchetypes, 1, -1 do
|
for i = #compatibleArchetypes, 1, -1 do
|
||||||
local archetype = compatibleArchetypes[i].archetype
|
local archetype = compatibleArchetypes[i][1]
|
||||||
local shouldRemove = false
|
local shouldRemove = false
|
||||||
for _, componentId in components do
|
for _, componentId in components do
|
||||||
if archetype.records[componentId] then
|
if archetype.records[componentId] then
|
||||||
|
@ -451,21 +447,21 @@ function World.query(world: World, ...: i53): Query
|
||||||
|
|
||||||
function preparedQuery:__iter()
|
function preparedQuery:__iter()
|
||||||
return function()
|
return function()
|
||||||
local archetype = compatibleArchetype.archetype
|
local archetype = compatibleArchetype[1]
|
||||||
local tr = compatibleArchetype.indices
|
|
||||||
local row = next(archetype.entities, lastRow)
|
local row = next(archetype.entities, lastRow)
|
||||||
while row == nil do
|
while row == nil do
|
||||||
lastArchetype, compatibleArchetype = next(compatibleArchetypes, lastArchetype)
|
lastArchetype, compatibleArchetype = next(compatibleArchetypes, lastArchetype)
|
||||||
if lastArchetype == nil then
|
if lastArchetype == nil then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
archetype = compatibleArchetype.archetype
|
archetype = compatibleArchetype[1]
|
||||||
row = next(archetype.entities, row)
|
row = next(archetype.entities, row)
|
||||||
end
|
end
|
||||||
lastRow = row
|
lastRow = row
|
||||||
|
|
||||||
local entityId = archetype.entities[row :: number]
|
local entityId = archetype.entities[row :: number]
|
||||||
local columns = archetype.columns
|
local columns = archetype.columns
|
||||||
|
local tr = compatibleArchetype[2]
|
||||||
|
|
||||||
if queryLength == 1 then
|
if queryLength == 1 then
|
||||||
return entityId, columns[tr[1]][row]
|
return entityId, columns[tr[1]][row]
|
||||||
|
|
438
mirror/init.lua
438
mirror/init.lua
|
@ -35,8 +35,19 @@ type EntityIndex = { [i24]: Record }
|
||||||
type ComponentIndex = { [i24]: ArchetypeMap}
|
type ComponentIndex = { [i24]: ArchetypeMap}
|
||||||
|
|
||||||
type ArchetypeRecord = number
|
type ArchetypeRecord = number
|
||||||
type ArchetypeMap = { map: { [ArchetypeId]: ArchetypeRecord } , size: number }
|
type ArchetypeMap = { sparse: { [ArchetypeId]: ArchetypeRecord } , size: number }
|
||||||
type Archetypes = { [ArchetypeId]: Archetype }
|
type Archetypes = { [ArchetypeId]: Archetype }
|
||||||
|
|
||||||
|
type ArchetypeDiff = {
|
||||||
|
added: Ty,
|
||||||
|
removed: Ty,
|
||||||
|
}
|
||||||
|
|
||||||
|
local HI_COMPONENT_ID = 256
|
||||||
|
local ON_ADD = HI_COMPONENT_ID + 1
|
||||||
|
local ON_REMOVE = HI_COMPONENT_ID + 2
|
||||||
|
local ON_SET = HI_COMPONENT_ID + 3
|
||||||
|
local REST = HI_COMPONENT_ID + 4
|
||||||
|
|
||||||
local function transitionArchetype(
|
local function transitionArchetype(
|
||||||
entityIndex: EntityIndex,
|
entityIndex: EntityIndex,
|
||||||
|
@ -59,11 +70,13 @@ local function transitionArchetype(
|
||||||
column[#column] = nil
|
column[#column] = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
destinationEntities[destinationRow] = sourceEntities[sourceRow]
|
destinationEntities[destinationRow] = sourceEntities[sourceRow]
|
||||||
local moveAway = #sourceEntities
|
entityIndex[sourceEntities[sourceRow]].row = destinationRow
|
||||||
sourceEntities[sourceRow] = sourceEntities[moveAway]
|
|
||||||
sourceEntities[moveAway] = nil
|
local movedAway = #sourceEntities
|
||||||
entityIndex[destinationEntities[destinationRow]].row = sourceRow
|
sourceEntities[sourceRow] = sourceEntities[movedAway]
|
||||||
|
entityIndex[sourceEntities[movedAway]].row = sourceRow
|
||||||
|
sourceEntities[movedAway] = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
local function archetypeAppend(entity: i53, archetype: Archetype): i24
|
local function archetypeAppend(entity: i53, archetype: Archetype): i24
|
||||||
|
@ -89,14 +102,7 @@ local function moveEntity(entityIndex, entityId: i53, record: Record, to: Archet
|
||||||
end
|
end
|
||||||
|
|
||||||
local function hash(arr): string | number
|
local function hash(arr): string | number
|
||||||
if true then
|
return table.concat(arr, "_")
|
||||||
return table.concat(arr, "_")
|
|
||||||
end
|
|
||||||
local hashed = 5381
|
|
||||||
for i = 1, #arr do
|
|
||||||
hashed = ((bit32.lshift(hashed, 5)) + hashed) + arr[i]
|
|
||||||
end
|
|
||||||
return hashed
|
|
||||||
end
|
end
|
||||||
|
|
||||||
local function createArchetypeRecords(componentIndex: ComponentIndex, to: Archetype, from: Archetype?)
|
local function createArchetypeRecords(componentIndex: ComponentIndex, to: Archetype, from: Archetype?)
|
||||||
|
@ -107,11 +113,11 @@ local function createArchetypeRecords(componentIndex: ComponentIndex, to: Archet
|
||||||
local destinationId = destinationIds[i]
|
local destinationId = destinationIds[i]
|
||||||
|
|
||||||
if not componentIndex[destinationId] then
|
if not componentIndex[destinationId] then
|
||||||
componentIndex[destinationId] = { size = 0, map = {} }
|
componentIndex[destinationId] = { size = 0, sparse = {} }
|
||||||
end
|
end
|
||||||
|
|
||||||
local archetypesMap = componentIndex[destinationId]
|
local archetypesMap = componentIndex[destinationId]
|
||||||
archetypesMap.map[to.id] = i
|
archetypesMap.sparse[to.id] = i
|
||||||
to.records[destinationId] = i
|
to.records[destinationId] = i
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -152,24 +158,48 @@ function World.new()
|
||||||
componentIndex = {},
|
componentIndex = {},
|
||||||
archetypes = {},
|
archetypes = {},
|
||||||
archetypeIndex = {},
|
archetypeIndex = {},
|
||||||
ROOT_ARCHETYPE = nil :: Archetype?,
|
ROOT_ARCHETYPE = (nil :: any) :: Archetype,
|
||||||
nextId = 0,
|
nextEntityId = 0,
|
||||||
nextArchetypeId = 0
|
nextComponentId = 0,
|
||||||
|
nextArchetypeId = 0,
|
||||||
|
hooks = {
|
||||||
|
[ON_ADD] = {}
|
||||||
|
}
|
||||||
}, World)
|
}, World)
|
||||||
self.ROOT_ARCHETYPE = archetypeOf(self, {}, nil)
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
type World = typeof(World.new())
|
local function emit(world, eventDescription)
|
||||||
|
local event = eventDescription.event
|
||||||
|
|
||||||
|
table.insert(world.hooks[event], {
|
||||||
|
ids = eventDescription.ids,
|
||||||
|
archetype = eventDescription.archetype,
|
||||||
|
otherArchetype = eventDescription.otherArchetype,
|
||||||
|
offset = eventDescription.offset
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
local function onNotifyAdd(world, archetype, otherArchetype, row: number, added: Ty)
|
||||||
|
if #added > 0 then
|
||||||
|
emit(world, {
|
||||||
|
event = ON_ADD,
|
||||||
|
ids = added,
|
||||||
|
archetype = archetype,
|
||||||
|
otherArchetype = otherArchetype,
|
||||||
|
offset = row,
|
||||||
|
})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
export type World = typeof(World.new())
|
||||||
|
|
||||||
local function ensureArchetype(world: World, types, prev)
|
local function ensureArchetype(world: World, types, prev)
|
||||||
if #types < 1 then
|
if #types < 1 then
|
||||||
|
return world.ROOT_ARCHETYPE
|
||||||
if not world.ROOT_ARCHETYPE then
|
|
||||||
local ROOT_ARCHETYPE = archetypeOf(world, {}, nil)
|
|
||||||
world.ROOT_ARCHETYPE = ROOT_ARCHETYPE
|
|
||||||
return ROOT_ARCHETYPE
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
local ty = hash(types)
|
local ty = hash(types)
|
||||||
local archetype = world.archetypeIndex[ty]
|
local archetype = world.archetypeIndex[ty]
|
||||||
|
@ -213,8 +243,14 @@ local function ensureEdge(archetype: Archetype, componentId: i53)
|
||||||
return archetype.edges[componentId]
|
return archetype.edges[componentId]
|
||||||
end
|
end
|
||||||
|
|
||||||
local function archetypeTraverseAdd(world: World, componentId: i53, archetype: Archetype?): Archetype
|
local function archetypeTraverseAdd(world: World, componentId: i53, from: Archetype): Archetype
|
||||||
local from = (archetype or world.ROOT_ARCHETYPE) :: Archetype
|
if not from then
|
||||||
|
if not world.ROOT_ARCHETYPE then
|
||||||
|
local ROOT_ARCHETYPE = archetypeOf(world, {}, nil)
|
||||||
|
world.ROOT_ARCHETYPE = ROOT_ARCHETYPE
|
||||||
|
end
|
||||||
|
from = world.ROOT_ARCHETYPE
|
||||||
|
end
|
||||||
local edge = ensureEdge(from, componentId)
|
local edge = ensureEdge(from, componentId)
|
||||||
|
|
||||||
if not edge.add then
|
if not edge.add then
|
||||||
|
@ -224,28 +260,34 @@ local function archetypeTraverseAdd(world: World, componentId: i53, archetype: A
|
||||||
return edge.add
|
return edge.add
|
||||||
end
|
end
|
||||||
|
|
||||||
function World.ensureRecord(world: World, entityId: i53)
|
local function ensureRecord(entityIndex, entityId: i53): Record
|
||||||
local entityIndex = world.entityIndex
|
|
||||||
local id = entityId
|
local id = entityId
|
||||||
if not entityIndex[id] then
|
if not entityIndex[id] then
|
||||||
entityIndex[id] = {} :: Record
|
entityIndex[id] = {}
|
||||||
end
|
end
|
||||||
return entityIndex[id]
|
return entityIndex[id] :: Record
|
||||||
end
|
end
|
||||||
|
|
||||||
function World.set(world: World, entityId: i53, componentId: i53, data: unknown)
|
function World.set(world: World, entityId: i53, componentId: i53, data: unknown)
|
||||||
local record = world:ensureRecord(entityId)
|
local record = ensureRecord(world.entityIndex, entityId)
|
||||||
local sourceArchetype = record.archetype
|
local sourceArchetype = record.archetype
|
||||||
local destinationArchetype = archetypeTraverseAdd(world, componentId, sourceArchetype)
|
local destinationArchetype = archetypeTraverseAdd(world, componentId, sourceArchetype)
|
||||||
|
|
||||||
if sourceArchetype and not (sourceArchetype == destinationArchetype) then
|
if sourceArchetype == destinationArchetype then
|
||||||
|
local archetypeRecord = destinationArchetype.records[componentId]
|
||||||
|
destinationArchetype.columns[archetypeRecord][record.row] = data
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if sourceArchetype then
|
||||||
moveEntity(world.entityIndex, entityId, record, destinationArchetype)
|
moveEntity(world.entityIndex, entityId, record, destinationArchetype)
|
||||||
else
|
else
|
||||||
-- if it has any components, then it wont be the root archetype
|
|
||||||
if #destinationArchetype.types > 0 then
|
if #destinationArchetype.types > 0 then
|
||||||
newEntity(entityId, record, destinationArchetype)
|
newEntity(entityId, record, destinationArchetype)
|
||||||
|
onNotifyAdd(world, destinationArchetype, sourceArchetype, record.row, { componentId })
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local archetypeRecord = destinationArchetype.records[componentId]
|
local archetypeRecord = destinationArchetype.records[componentId]
|
||||||
destinationArchetype.columns[archetypeRecord][record.row] = data
|
destinationArchetype.columns[archetypeRecord][record.row] = data
|
||||||
end
|
end
|
||||||
|
@ -265,7 +307,7 @@ local function archetypeTraverseRemove(world: World, componentId: i53, archetype
|
||||||
end
|
end
|
||||||
|
|
||||||
function World.remove(world: World, entityId: i53, componentId: i53)
|
function World.remove(world: World, entityId: i53, componentId: i53)
|
||||||
local record = world:ensureRecord(entityId)
|
local record = ensureRecord(world.entityIndex, entityId)
|
||||||
local sourceArchetype = record.archetype
|
local sourceArchetype = record.archetype
|
||||||
local destinationArchetype = archetypeTraverseRemove(world, componentId, sourceArchetype)
|
local destinationArchetype = archetypeTraverseRemove(world, componentId, sourceArchetype)
|
||||||
|
|
||||||
|
@ -276,7 +318,7 @@ end
|
||||||
|
|
||||||
local function get(componentIndex: { [i24]: ArchetypeMap }, record: Record, componentId: i24)
|
local function get(componentIndex: { [i24]: ArchetypeMap }, record: Record, componentId: i24)
|
||||||
local archetype = record.archetype
|
local archetype = record.archetype
|
||||||
local archetypeRecord = componentIndex[componentId].map[archetype.id]
|
local archetypeRecord = componentIndex[componentId].sparse[archetype.id]
|
||||||
|
|
||||||
if not archetypeRecord then
|
if not archetypeRecord then
|
||||||
return nil
|
return nil
|
||||||
|
@ -308,149 +350,249 @@ function World.get(world: World, entityId: i53, a: i53, b: i53?, c: i53?, d: i53
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function World.entity(world: World)
|
local function noop(self: Query, ...: i53): () -> (number, ...any)
|
||||||
world.nextId += 1
|
return function()
|
||||||
return world.nextId
|
end :: any
|
||||||
end
|
end
|
||||||
|
|
||||||
local function noop(): any
|
local EmptyQuery = {
|
||||||
return function()
|
__iter = noop,
|
||||||
end
|
without = noop
|
||||||
end
|
}
|
||||||
|
EmptyQuery.__index = EmptyQuery
|
||||||
|
setmetatable(EmptyQuery, EmptyQuery)
|
||||||
|
|
||||||
local function getSmallestMap(componentIndex, components)
|
export type Query = typeof(EmptyQuery)
|
||||||
local s: any
|
|
||||||
|
|
||||||
for i, componentId in components do
|
function World.query(world: World, ...: i53): Query
|
||||||
local map = componentIndex[componentId]
|
|
||||||
if s == nil or map.size < s.size then
|
|
||||||
s = map
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return s.map
|
|
||||||
end
|
|
||||||
|
|
||||||
function World.query(world: World, ...: i53): (() -> (number, ...any)) | () -> ()
|
|
||||||
local compatibleArchetypes = {}
|
local compatibleArchetypes = {}
|
||||||
local components = { ... }
|
local components = { ... }
|
||||||
local archetypes = world.archetypes
|
local archetypes = world.archetypes
|
||||||
local queryLength = #components
|
local queryLength = #components
|
||||||
local firstArchetypeMap = getSmallestMap(world.componentIndex, components)
|
|
||||||
|
|
||||||
if not firstArchetypeMap then
|
if queryLength == 0 then
|
||||||
return noop()
|
error("Missing components")
|
||||||
end
|
end
|
||||||
|
|
||||||
for id in firstArchetypeMap do
|
local firstArchetypeMap
|
||||||
|
local componentIndex = world.componentIndex
|
||||||
|
|
||||||
|
for i, componentId in components do
|
||||||
|
local map = componentIndex[componentId]
|
||||||
|
if not map then
|
||||||
|
return EmptyQuery
|
||||||
|
end
|
||||||
|
|
||||||
|
if firstArchetypeMap == nil or map.size < firstArchetypeMap.size then
|
||||||
|
firstArchetypeMap = map
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local i = 0
|
||||||
|
for id in firstArchetypeMap.sparse do
|
||||||
local archetype = archetypes[id]
|
local archetype = archetypes[id]
|
||||||
local columns = archetype.columns
|
|
||||||
local archetypeRecords = archetype.records
|
local archetypeRecords = archetype.records
|
||||||
local indices = {}
|
local indices = {}
|
||||||
local skip = false
|
local skip = false
|
||||||
|
|
||||||
for i, componentId in components do
|
for j, componentId in components do
|
||||||
local index = archetypeRecords[componentId]
|
local index = archetypeRecords[componentId]
|
||||||
if not index then
|
if not index then
|
||||||
skip = true
|
skip = true
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
indices[i] = columns[index]
|
indices[j] = archetypeRecords[componentId]
|
||||||
end
|
end
|
||||||
|
|
||||||
if skip then
|
if skip then
|
||||||
continue
|
continue
|
||||||
end
|
end
|
||||||
|
i += 1
|
||||||
table.insert(compatibleArchetypes, {
|
table.insert(compatibleArchetypes, { archetype, indices })
|
||||||
archetype = archetype,
|
|
||||||
indices = indices
|
|
||||||
})
|
|
||||||
end
|
end
|
||||||
|
|
||||||
local lastArchetype, compatibleArchetype = next(compatibleArchetypes)
|
local lastArchetype, compatibleArchetype = next(compatibleArchetypes)
|
||||||
if not compatibleArchetype then
|
if not lastArchetype then
|
||||||
return noop()
|
return EmptyQuery
|
||||||
end
|
end
|
||||||
|
|
||||||
local lastRow
|
local preparedQuery = {}
|
||||||
|
preparedQuery.__index = preparedQuery
|
||||||
return function()
|
|
||||||
local archetype = compatibleArchetype.archetype
|
function preparedQuery:without(...)
|
||||||
local indices = compatibleArchetype.indices
|
local components = { ... }
|
||||||
local row = next(archetype.entities, lastRow)
|
for i = #compatibleArchetypes, 1, -1 do
|
||||||
while row == nil do
|
local archetype = compatibleArchetypes[i][1]
|
||||||
lastArchetype, compatibleArchetype = next(compatibleArchetypes, lastArchetype)
|
local shouldRemove = false
|
||||||
if lastArchetype == nil then
|
for _, componentId in components do
|
||||||
return
|
if archetype.records[componentId] then
|
||||||
|
shouldRemove = true
|
||||||
|
break
|
||||||
|
end
|
||||||
end
|
end
|
||||||
archetype = compatibleArchetype.archetype
|
if shouldRemove then
|
||||||
row = next(archetype.entities, row)
|
table.remove(compatibleArchetypes, i)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
lastArchetype, compatibleArchetype = next(compatibleArchetypes)
|
||||||
|
if not lastArchetype then
|
||||||
|
return EmptyQuery
|
||||||
end
|
end
|
||||||
lastRow = row
|
|
||||||
|
|
||||||
local entityId = archetype.entities[row :: number]
|
return self
|
||||||
|
|
||||||
if queryLength == 1 then
|
|
||||||
return entityId, indices[1][row]
|
|
||||||
elseif queryLength == 2 then
|
|
||||||
return entityId, indices[1][row], indices[2][row]
|
|
||||||
elseif queryLength == 3 then
|
|
||||||
return entityId,
|
|
||||||
indices[1][row],
|
|
||||||
indices[2][row],
|
|
||||||
indices[3][row]
|
|
||||||
elseif queryLength == 4 then
|
|
||||||
return entityId,
|
|
||||||
indices[1][row],
|
|
||||||
indices[2][row],
|
|
||||||
indices[3][row],
|
|
||||||
indices[4][row]
|
|
||||||
elseif queryLength == 5 then
|
|
||||||
return entityId,
|
|
||||||
indices[1][row],
|
|
||||||
indices[2][row],
|
|
||||||
indices[3][row],
|
|
||||||
indices[4][row]
|
|
||||||
elseif queryLength == 6 then
|
|
||||||
return entityId,
|
|
||||||
indices[1][row],
|
|
||||||
indices[2][row],
|
|
||||||
indices[3][row],
|
|
||||||
indices[4][row],
|
|
||||||
indices[5][row],
|
|
||||||
indices[6][row]
|
|
||||||
elseif queryLength == 7 then
|
|
||||||
return entityId,
|
|
||||||
indices[1][row],
|
|
||||||
indices[2][row],
|
|
||||||
indices[3][row],
|
|
||||||
indices[4][row],
|
|
||||||
indices[5][row],
|
|
||||||
indices[6][row],
|
|
||||||
indices[7][row]
|
|
||||||
|
|
||||||
elseif queryLength == 8 then
|
|
||||||
return entityId,
|
|
||||||
indices[1][row],
|
|
||||||
indices[2][row],
|
|
||||||
indices[3][row],
|
|
||||||
indices[4][row],
|
|
||||||
indices[5][row],
|
|
||||||
indices[6][row],
|
|
||||||
indices[7][row],
|
|
||||||
indices[8][row]
|
|
||||||
end
|
|
||||||
|
|
||||||
local queryOutput = {}
|
|
||||||
for i, componentId in components do
|
|
||||||
queryOutput[i] = indices[i][row]
|
|
||||||
end
|
|
||||||
|
|
||||||
return entityId, unpack(queryOutput, 1, queryLength)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local lastRow
|
||||||
|
local queryOutput = {}
|
||||||
|
|
||||||
|
|
||||||
|
function preparedQuery:__iter()
|
||||||
|
return function()
|
||||||
|
local archetype = compatibleArchetype[1]
|
||||||
|
local row = next(archetype.entities, lastRow)
|
||||||
|
while row == nil do
|
||||||
|
lastArchetype, compatibleArchetype = next(compatibleArchetypes, lastArchetype)
|
||||||
|
if lastArchetype == nil then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
archetype = compatibleArchetype[1]
|
||||||
|
row = next(archetype.entities, row)
|
||||||
|
end
|
||||||
|
lastRow = row
|
||||||
|
|
||||||
|
local entityId = archetype.entities[row :: number]
|
||||||
|
local columns = archetype.columns
|
||||||
|
local tr = compatibleArchetype[2]
|
||||||
|
|
||||||
|
if queryLength == 1 then
|
||||||
|
return entityId, columns[tr[1]][row]
|
||||||
|
elseif queryLength == 2 then
|
||||||
|
return entityId, columns[tr[1]][row], columns[tr[2]][row]
|
||||||
|
elseif queryLength == 3 then
|
||||||
|
return entityId,
|
||||||
|
columns[tr[1]][row],
|
||||||
|
columns[tr[2]][row],
|
||||||
|
columns[tr[3]][row]
|
||||||
|
elseif queryLength == 4 then
|
||||||
|
return entityId,
|
||||||
|
columns[tr[1]][row],
|
||||||
|
columns[tr[2]][row],
|
||||||
|
columns[tr[3]][row],
|
||||||
|
columns[tr[4]][row]
|
||||||
|
elseif queryLength == 5 then
|
||||||
|
return entityId,
|
||||||
|
columns[tr[1]][row],
|
||||||
|
columns[tr[2]][row],
|
||||||
|
columns[tr[3]][row],
|
||||||
|
columns[tr[4]][row],
|
||||||
|
columns[tr[5]][row]
|
||||||
|
elseif queryLength == 6 then
|
||||||
|
return entityId,
|
||||||
|
columns[tr[1]][row],
|
||||||
|
columns[tr[2]][row],
|
||||||
|
columns[tr[3]][row],
|
||||||
|
columns[tr[4]][row],
|
||||||
|
columns[tr[5]][row],
|
||||||
|
columns[tr[6]][row]
|
||||||
|
elseif queryLength == 7 then
|
||||||
|
return entityId,
|
||||||
|
columns[tr[1]][row],
|
||||||
|
columns[tr[2]][row],
|
||||||
|
columns[tr[3]][row],
|
||||||
|
columns[tr[4]][row],
|
||||||
|
columns[tr[5]][row],
|
||||||
|
columns[tr[6]][row],
|
||||||
|
columns[tr[7]][row]
|
||||||
|
elseif queryLength == 8 then
|
||||||
|
return entityId,
|
||||||
|
columns[tr[1]][row],
|
||||||
|
columns[tr[2]][row],
|
||||||
|
columns[tr[3]][row],
|
||||||
|
columns[tr[4]][row],
|
||||||
|
columns[tr[5]][row],
|
||||||
|
columns[tr[6]][row],
|
||||||
|
columns[tr[7]][row],
|
||||||
|
columns[tr[8]][row]
|
||||||
|
end
|
||||||
|
|
||||||
|
for i in components do
|
||||||
|
queryOutput[i] = tr[i][row]
|
||||||
|
end
|
||||||
|
|
||||||
|
return entityId, unpack(queryOutput, 1, queryLength)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return setmetatable({}, preparedQuery) :: any
|
||||||
end
|
end
|
||||||
|
|
||||||
return {
|
function World.component(world: World)
|
||||||
World = World
|
local componentId = world.nextComponentId + 1
|
||||||
}
|
if componentId > HI_COMPONENT_ID then
|
||||||
|
error("Too many components")
|
||||||
|
end
|
||||||
|
world.nextComponentId = componentId
|
||||||
|
return componentId
|
||||||
|
end
|
||||||
|
|
||||||
|
function World.entity(world: World)
|
||||||
|
world.nextEntityId += 1
|
||||||
|
return world.nextEntityId + REST
|
||||||
|
end
|
||||||
|
|
||||||
|
function World.observer(world: World, ...)
|
||||||
|
local componentIds = { ... }
|
||||||
|
|
||||||
|
return {
|
||||||
|
event = function(event)
|
||||||
|
local hook = world.hooks[event]
|
||||||
|
world.hooks[event] = nil
|
||||||
|
|
||||||
|
local last, change
|
||||||
|
return function()
|
||||||
|
last, change = next(hook, last)
|
||||||
|
if not last then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local matched = false
|
||||||
|
|
||||||
|
while not matched do
|
||||||
|
local skip = false
|
||||||
|
for _, id in change.ids do
|
||||||
|
if not table.find(componentIds, id) then
|
||||||
|
skip = true
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if skip then
|
||||||
|
last, change = next(hook, last)
|
||||||
|
continue
|
||||||
|
end
|
||||||
|
|
||||||
|
matched = true
|
||||||
|
end
|
||||||
|
|
||||||
|
local queryOutput = {}
|
||||||
|
local row = change.offset
|
||||||
|
local archetype = change.archetype
|
||||||
|
local columns = archetype.columns
|
||||||
|
local archetypeRecords = archetype.records
|
||||||
|
for _, id in componentIds do
|
||||||
|
table.insert(queryOutput, columns[archetypeRecords[id]][row])
|
||||||
|
end
|
||||||
|
|
||||||
|
return archetype.entities[row], unpack(queryOutput, 1, #queryOutput)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
return table.freeze({
|
||||||
|
World = World,
|
||||||
|
ON_ADD = ON_ADD,
|
||||||
|
ON_REMOVE = ON_REMOVE,
|
||||||
|
ON_SET = ON_SET
|
||||||
|
})
|
||||||
|
|
Loading…
Reference in a new issue