mirror of
https://github.com/Ukendio/jecs.git
synced 2025-04-25 01:20:04 +00:00
Update newMatter.lua to match world-rewrite branch
This commit is contained in:
parent
089c5d46a9
commit
a2f47ae7aa
1 changed files with 10 additions and 253 deletions
263
newMatter.lua
263
newMatter.lua
|
@ -43,57 +43,6 @@ local function values(dictionary)
|
||||||
return valuesList
|
return valuesList
|
||||||
end
|
end
|
||||||
|
|
||||||
local valueIds = {}
|
|
||||||
local nextValueId = 0
|
|
||||||
local compatibilityCache = {}
|
|
||||||
local archetypeCache = {}
|
|
||||||
|
|
||||||
local function getValueId(value)
|
|
||||||
local valueId = valueIds[value]
|
|
||||||
if valueId == nil then
|
|
||||||
valueIds[value] = nextValueId
|
|
||||||
valueId = nextValueId
|
|
||||||
nextValueId += 1
|
|
||||||
end
|
|
||||||
|
|
||||||
return valueId
|
|
||||||
end
|
|
||||||
|
|
||||||
function archetypeOf(...)
|
|
||||||
local length = select("#", ...)
|
|
||||||
|
|
||||||
local currentNode = archetypeCache
|
|
||||||
|
|
||||||
for i = 1, length do
|
|
||||||
local nextNode = currentNode[select(i, ...)]
|
|
||||||
|
|
||||||
if not nextNode then
|
|
||||||
nextNode = {}
|
|
||||||
currentNode[select(i, ...)] = nextNode
|
|
||||||
end
|
|
||||||
|
|
||||||
currentNode = nextNode
|
|
||||||
end
|
|
||||||
|
|
||||||
if currentNode._archetype then
|
|
||||||
return currentNode._archetype
|
|
||||||
end
|
|
||||||
|
|
||||||
local list = table.create(length)
|
|
||||||
|
|
||||||
for i = 1, length do
|
|
||||||
list[i] = getValueId(select(i, ...))
|
|
||||||
end
|
|
||||||
|
|
||||||
table.sort(list)
|
|
||||||
|
|
||||||
local archetype = table.concat(list, "_")
|
|
||||||
|
|
||||||
currentNode._archetype = archetype
|
|
||||||
|
|
||||||
return archetype
|
|
||||||
end
|
|
||||||
|
|
||||||
local stack = {}
|
local stack = {}
|
||||||
|
|
||||||
local function newStackFrame(node)
|
local function newStackFrame(node)
|
||||||
|
@ -717,14 +666,16 @@ end
|
||||||
|
|
||||||
local function get(componentIndex: ComponentIndex, record: Record, componentId: i24): ComponentInstance?
|
local function get(componentIndex: ComponentIndex, record: Record, componentId: i24): ComponentInstance?
|
||||||
local archetype = record.archetype
|
local archetype = record.archetype
|
||||||
if not archetype then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
local map = componentIndex[componentId]
|
local map = componentIndex[componentId]
|
||||||
if map == nil then
|
if map == nil then
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if archetype == nil then
|
||||||
|
-- TODO... what?
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
local archetypeRecord = map.sparse[archetype.id]
|
local archetypeRecord = map.sparse[archetype.id]
|
||||||
if not archetypeRecord then
|
if not archetypeRecord then
|
||||||
return nil
|
return nil
|
||||||
|
@ -1272,7 +1223,8 @@ end
|
||||||
|
|
||||||
@param ... Component -- The component types to query. Only entities with *all* of these components will be returned.
|
@param ... Component -- The component types to query. Only entities with *all* of these components will be returned.
|
||||||
@return QueryResult -- See [QueryResult](/api/QueryResult) docs.
|
@return QueryResult -- See [QueryResult](/api/QueryResult) docs.
|
||||||
]=]function World.query(world: World, ...: Component): any
|
]=]
|
||||||
|
function World.query(world: World, ...: Component): any
|
||||||
local compatibleArchetypes = {}
|
local compatibleArchetypes = {}
|
||||||
local components = { ... }
|
local components = { ... }
|
||||||
local archetypes = world.archetypes
|
local archetypes = world.archetypes
|
||||||
|
@ -1280,9 +1232,7 @@ end
|
||||||
local a: any, b: any, c: any, d: any, e: any = ...
|
local a: any, b: any, c: any, d: any, e: any = ...
|
||||||
|
|
||||||
if queryLength == 0 then
|
if queryLength == 0 then
|
||||||
-- TODO:
|
return emptyQueryResult
|
||||||
-- return noop query
|
|
||||||
warn("TODO noop query")
|
|
||||||
end
|
end
|
||||||
|
|
||||||
if queryLength == 1 then
|
if queryLength == 1 then
|
||||||
|
@ -1380,10 +1330,7 @@ end
|
||||||
for _, componentId in (components :: any) :: { number } do
|
for _, componentId in (components :: any) :: { number } do
|
||||||
local map = componentIndex[componentId]
|
local map = componentIndex[componentId]
|
||||||
if not map then
|
if not map then
|
||||||
-- TODO:
|
return emptyQueryResult
|
||||||
-- see what upstream does in this case
|
|
||||||
-- currently replicating jecs
|
|
||||||
error(tostring(componentId) .. " has not been added to an entity")
|
|
||||||
end
|
end
|
||||||
|
|
||||||
if firstArchetypeMap == nil or map.size < firstArchetypeMap.size then
|
if firstArchetypeMap == nil or map.size < firstArchetypeMap.size then
|
||||||
|
@ -1407,197 +1354,7 @@ end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Only want to include archetype selection?
|
return queryResult(compatibleArchetypes, components :: any, queryLength, a, b, c, d, e)
|
||||||
|
|
||||||
local lastArchetype, archetype = next(compatibleArchetypes)
|
|
||||||
if not lastArchetype then
|
|
||||||
return noop()
|
|
||||||
end
|
|
||||||
|
|
||||||
local lastRow
|
|
||||||
local queryOutput = {}
|
|
||||||
local function iterate()
|
|
||||||
local row = next(archetype.entities, lastRow)
|
|
||||||
while row == nil do
|
|
||||||
lastArchetype, archetype = next(compatibleArchetypes, lastArchetype)
|
|
||||||
if lastArchetype == nil then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
row = next(archetype.entities, row)
|
|
||||||
end
|
|
||||||
|
|
||||||
lastRow = row
|
|
||||||
|
|
||||||
local columns = archetype.columns
|
|
||||||
local entityId = archetype.entities[row :: number]
|
|
||||||
local archetypeRecords = archetype.records
|
|
||||||
|
|
||||||
if queryLength == 1 then
|
|
||||||
return entityId, columns[archetypeRecords[a]][row]
|
|
||||||
elseif queryLength == 2 then
|
|
||||||
return entityId, columns[archetypeRecords[a]][row], columns[archetypeRecords[b]][row]
|
|
||||||
elseif queryLength == 3 then
|
|
||||||
return entityId,
|
|
||||||
columns[archetypeRecords[a]][row],
|
|
||||||
columns[archetypeRecords[b]][row],
|
|
||||||
columns[archetypeRecords[c]][row]
|
|
||||||
elseif queryLength == 4 then
|
|
||||||
return entityId,
|
|
||||||
columns[archetypeRecords[a]][row],
|
|
||||||
columns[archetypeRecords[b]][row],
|
|
||||||
columns[archetypeRecords[c]][row],
|
|
||||||
columns[archetypeRecords[d]][row]
|
|
||||||
elseif queryLength == 5 then
|
|
||||||
return entityId,
|
|
||||||
columns[archetypeRecords[a]][row],
|
|
||||||
columns[archetypeRecords[b]][row],
|
|
||||||
columns[archetypeRecords[c]][row],
|
|
||||||
columns[archetypeRecords[d]][row],
|
|
||||||
columns[archetypeRecords[e]][row]
|
|
||||||
end
|
|
||||||
|
|
||||||
for i, componentId in components do
|
|
||||||
queryOutput[i] = columns[archetypeRecords[componentId]][row]
|
|
||||||
end
|
|
||||||
|
|
||||||
return entityId, unpack(queryOutput, 1, queryLength)
|
|
||||||
end
|
|
||||||
|
|
||||||
--[=[
|
|
||||||
@class QueryResult
|
|
||||||
|
|
||||||
A result from the [`World:query`](/api/World#query) function.
|
|
||||||
|
|
||||||
Calling the table or the `next` method allows iteration over the results. Once all results have been returned, the
|
|
||||||
QueryResult is exhausted and is no longer useful.
|
|
||||||
|
|
||||||
```lua
|
|
||||||
for id, enemy, charge, model in world:query(Enemy, Charge, Model) do
|
|
||||||
-- Do something
|
|
||||||
end
|
|
||||||
```
|
|
||||||
]=]
|
|
||||||
local QueryResult = {}
|
|
||||||
QueryResult.__index = QueryResult
|
|
||||||
|
|
||||||
function QueryResult:__call()
|
|
||||||
return iterate()
|
|
||||||
end
|
|
||||||
|
|
||||||
function QueryResult:__iter()
|
|
||||||
return function()
|
|
||||||
return iterate()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
--[=[
|
|
||||||
Returns the next set of values from the query result. Once all results have been returned, the
|
|
||||||
QueryResult is exhausted and is no longer useful.
|
|
||||||
|
|
||||||
:::info
|
|
||||||
This function is equivalent to calling the QueryResult as a function. When used in a for loop, this is implicitly
|
|
||||||
done by the language itself.
|
|
||||||
:::
|
|
||||||
|
|
||||||
```lua
|
|
||||||
-- Using world:query in this position will make Lua invoke the table as a function. This is conventional.
|
|
||||||
for id, enemy, charge, model in world:query(Enemy, Charge, Model) do
|
|
||||||
-- Do something
|
|
||||||
end
|
|
||||||
```
|
|
||||||
|
|
||||||
If you wanted to iterate over the QueryResult without a for loop, it's recommended that you call `next` directly
|
|
||||||
instead of calling the QueryResult as a function.
|
|
||||||
```lua
|
|
||||||
local id, enemy, charge, model = world:query(Enemy, Charge, Model):next()
|
|
||||||
local id, enemy, charge, model = world:query(Enemy, Charge, Model)() -- Possible, but unconventional
|
|
||||||
```
|
|
||||||
|
|
||||||
@return id -- Entity ID
|
|
||||||
@return ...ComponentInstance -- The requested component values
|
|
||||||
]=]
|
|
||||||
function QueryResult:next()
|
|
||||||
return iterate()
|
|
||||||
end
|
|
||||||
|
|
||||||
local Snapshot = {
|
|
||||||
__iter = function(self): any
|
|
||||||
local i = 0
|
|
||||||
return function()
|
|
||||||
i += 1
|
|
||||||
|
|
||||||
local data = self[i]
|
|
||||||
|
|
||||||
if data then
|
|
||||||
return unpack(data, 1, data.n)
|
|
||||||
end
|
|
||||||
|
|
||||||
return
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
}
|
|
||||||
|
|
||||||
function QueryResult:snapshot()
|
|
||||||
local list = setmetatable({}, Snapshot) :: any
|
|
||||||
|
|
||||||
local function iter()
|
|
||||||
--local entry = table.pack(iterate())
|
|
||||||
--return if #entry == 0 then nil else entry
|
|
||||||
return "x"
|
|
||||||
end
|
|
||||||
|
|
||||||
for data in iter :: any do
|
|
||||||
if data[1] then
|
|
||||||
table.insert(list, data)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return list
|
|
||||||
end
|
|
||||||
|
|
||||||
--[=[
|
|
||||||
Returns an iterator that will skip any entities that also have the given components.
|
|
||||||
|
|
||||||
@param ... Component -- The component types to filter against.
|
|
||||||
@return () -> (id, ...ComponentInstance) -- Iterator of entity ID followed by the requested component values
|
|
||||||
|
|
||||||
```lua
|
|
||||||
for id in world:query(Target):without(Model) do
|
|
||||||
-- Do something
|
|
||||||
end
|
|
||||||
```
|
|
||||||
]=]
|
|
||||||
function QueryResult:without(...)
|
|
||||||
local components = { ... }
|
|
||||||
for i, component in components do
|
|
||||||
components[i] = #component
|
|
||||||
end
|
|
||||||
|
|
||||||
local compatibleArchetypes = compatibleArchetypes
|
|
||||||
for i = #compatibleArchetypes, 1, -1 do
|
|
||||||
local archetype = compatibleArchetypes[i]
|
|
||||||
local shouldRemove = false
|
|
||||||
for _, componentId in components do
|
|
||||||
if archetype.records[componentId] then
|
|
||||||
shouldRemove = true
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if shouldRemove then
|
|
||||||
table.remove(compatibleArchetypes, i)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
lastArchetype, archetype = next(compatibleArchetypes)
|
|
||||||
if not lastArchetype then
|
|
||||||
return noop()
|
|
||||||
end
|
|
||||||
|
|
||||||
return self
|
|
||||||
end
|
|
||||||
|
|
||||||
return setmetatable({}, QueryResult)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
local function cleanupQueryChanged(hookState)
|
local function cleanupQueryChanged(hookState)
|
||||||
|
|
Loading…
Reference in a new issue