Initial commit

This commit is contained in:
Ukendio 2024-07-26 02:46:09 +02:00
parent 0292574b5f
commit b4a10b10b9
3 changed files with 202 additions and 157 deletions

2
.gitignore vendored
View file

@ -53,7 +53,7 @@ WallyPatches
# Misc
roblox.toml
sourcemap.json
drafts/*.lua
drafts/
# Cached Vitepress (docs)

View file

@ -1,4 +1,3 @@
--!optimize 2
--!native
--!strict
@ -17,6 +16,7 @@ type ArchetypeEdge = {
remove: Archetype,
}
type Archetype = {
id: number,
edges: { [i53]: ArchetypeEdge },
@ -669,6 +669,32 @@ do
end
end
local world_has: () -> boolean
do
function world_has(world, entity_id, ...)
local id = entity_id
local record = world.entityIndex.sparse[id]
if not record then
return false
end
local archetype = record.archetype
if not archetype then
return false
end
local tr = archetype.records
for i = 1, select("#", ...) do
if not tr[select(i, ...)] then
return false
end
end
return true
end
end
type Item = () -> (number, ...any)
export type Query = typeof({
__iter = function(): Item
@ -678,8 +704,8 @@ export type Query = typeof({
end,
}) & {
next: Item,
replace: (Query, ...any) -> (),
without: (Query) -> Query
without: (Query) -> Query,
replace: (Query, (...any) -> (...any)) -> ()
}
type CompatibleArchetype = { archetype: Archetype, indices: { number } }
@ -704,29 +730,25 @@ do
setmetatable(EmptyQuery, EmptyQuery)
local indices: { { number } }
local compatibleArchetypes: { Archetype }
local length
local components: { number }
local queryLength: number
local lastArchetype: number
local archetype: Archetype
local queryOutput: { any }
local entities: {}
local queryLength: number
local entities: { number }
local i: number
local function world_query_next()
local compatible_archetypes: { Archetype }
local column_indices: { { number} }
local ids: { number }
local function world_query_next(): any
local entityId = entities[i]
while entityId == nil do
lastArchetype += 1
archetype = compatibleArchetypes[lastArchetype]
archetype = compatible_archetypes[lastArchetype]
if not archetype then
return
return nil
end
entities = archetype.entities
i = #entities
entityId = entities[i]
@ -736,7 +758,7 @@ do
i-=1
local columns = archetype.columns
local tr = indices[lastArchetype]
local tr = column_indices[lastArchetype]
if queryLength == 1 then
return entityId, columns[tr[1]][row]
@ -747,36 +769,56 @@ do
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],
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],
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],
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],
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] = columns[tr[i]][row]
for j in ids do
queryOutput[j] = columns[tr[j]][row]
end
return entityId, unpack(queryOutput, 1, queryLength)
end
local function world_query_without(self, ...): Query
local function world_query_iter()
return world_query_next
end
local function world_query_without(self, ...)
local withoutComponents = { ... }
for i = #compatibleArchetypes, 1, -1 do
local archetype = compatibleArchetypes[i]
for i = #compatible_archetypes, 1, -1 do
local archetype = compatible_archetypes[i]
local records = archetype.records
local shouldRemove = false
@ -788,35 +830,29 @@ do
end
if shouldRemove then
table.remove(compatibleArchetypes, i)
table.remove(compatible_archetypes, i)
end
end
if #compatibleArchetypes == 0 then
lastArchetype = 1
archetype = compatible_archetypes[lastArchetype]
if not archetype then
return EmptyQuery
end
return self
end
local function world_query_iter()
lastArchetype = 1
archetype = compatibleArchetypes[1]
entities = archetype.entities
i = #entities
return world_query_next
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(_, fn: any)
for i, archetype in compatibleArchetypes do
local tr = indices[i]
local function world_query_replace(_, fn: (...any) -> (...any))
for i, archetype in compatible_archetypes do
local tr = column_indices[i]
local columns = archetype.columns
for row in archetype.entities do
@ -855,28 +891,30 @@ do
end
end
function world_query(world: World, ...: number): Query
function world_query(world: World, ...: any): Query
-- breaking?
if (...) == nil then
error("Missing components")
end
indices = {}
compatibleArchetypes = {}
length = 0
components = { ... }
local indices = {}
local compatibleArchetypes = {}
local length = 0
local components = { ... } :: any
local archetypes = world.archetypes
local archetypes: { Archetype } = world.archetypes :: any
local firstArchetypeMap: ArchetypeMap
local componentIndex = world.componentIndex
for _, componentId in components do
local map: ArchetypeMap = componentIndex[componentId] :: any
local map = componentIndex[componentId]
if not map then
return EmptyQuery
end
if (firstArchetypeMap :: any) == nil or firstArchetypeMap.size > map.size then
if firstArchetypeMap == nil or map.size < firstArchetypeMap.size then
firstArchetypeMap = map
end
end
@ -885,7 +923,7 @@ do
local compatibleArchetype = archetypes[id]
local archetypeRecords = compatibleArchetype.records
local records: { number } = {}
local records = {}
local skip = false
for i, componentId in components do
@ -907,15 +945,19 @@ do
indices[length] = records
end
compatible_archetypes = compatibleArchetypes
column_indices = indices
ids = components
lastArchetype = 1
archetype = compatibleArchetypes[lastArchetype]
archetype = compatible_archetypes[lastArchetype]
if not archetype then
return EmptyQuery
end
queryOutput = {}
queryLength = #components
queryLength = #ids
entities = archetype.entities
i = #entities
@ -925,9 +967,11 @@ do
next = world_query_next,
without = world_query_without,
replace = world_query_replace
}
} :: any
return setmetatable(it, it) :: any
setmetatable(it, it)
return it
end
end
@ -1077,6 +1121,7 @@ World.component = world_component
World.add = world_add
World.set = world_set
World.get = world_get
World.has = world_has
World.target = world_target
World.parent = world_parent

View file

@ -60,10 +60,9 @@ TEST("world", function()
world:clear(e)
CHECK(world:get(e, A) == nil)
CHECK(world:get(e, B) == nil)
end
do CASE("iterator should not drain the query")
do CASE("should drain query while iterating")
local world = jecs.World.new() :: World
local A = world:component()
local B = world:component()
@ -85,7 +84,8 @@ TEST("world", function()
for _ in q do
j+=1
end
CHECK(i == j)
CHECK(i == 2)
CHECK(j == 0)
end
do CASE("should be able to get next results")