Cleanup functions and add missing types

This commit is contained in:
kalrnlo 2024-06-23 19:55:50 -04:00
parent ca8e1376ca
commit 447c7f12e7

View file

@ -11,14 +11,14 @@ type ArchetypeId = number
type Column = { any } type Column = { any }
type ArchetypeEdge = {
add: Archetype,
remove: Archetype,
}
type Archetype = { type Archetype = {
id: number, id: number,
edges: { edges: { [i53]: ArchetypeEdge },
[i53]: {
add: Archetype,
remove: Archetype,
},
},
types: Ty, types: Ty,
type: string | number, type: string | number,
entities: { number }, entities: { number },
@ -75,7 +75,7 @@ local ECS_ID_FLAGS_MASK = 0x10
local ECS_ENTITY_MASK = bit32.lshift(1, 24) local ECS_ENTITY_MASK = bit32.lshift(1, 24)
local ECS_GENERATION_MASK = bit32.lshift(1, 16) local ECS_GENERATION_MASK = bit32.lshift(1, 16)
local function addFlags(isPair: boolean) local function addFlags(isPair: boolean): number
local typeFlags = 0x0 local typeFlags = 0x0
if isPair then if isPair then
@ -94,48 +94,17 @@ local function addFlags(isPair: boolean)
return typeFlags return typeFlags
end end
<<<<<<< HEAD local function ECS_COMBINE(source: number, target: number): i53
local function newId(source: number, target: number): number return ((source * 268435456) + target) * ECS_ID_FLAGS_MASK
return ((source * 2^28) + target) * ECS_ID_FLAGS_MASK
end end
local function ECS_IS_PAIR(e: number): boolean local function ECS_IS_PAIR(e: number): boolean
return ( (e % 2^4) // FLAGS_PAIR ) ~= 0 return if e > ECS_ENTITY_MASK then (((e % 2) ^ 4) // FLAGS_PAIR) ~= 0 else false
end
local function separate(entity: number): (number, number, number)
local type_flags = entity % 0x10
local entity = entity // ECS_ID_FLAGS_MASK
return new_entity // ECS_ENTITY_MASK, new_entity % ECS_GENERATION_MASK, type_flags
=======
local function ECS_COMBINE(source: number, target: number): i53
local e = source * 268435456 + target * ECS_ID_FLAGS_MASK
return e
end
local function ECS_IS_PAIR(e: number)
if e > ECS_ENTITY_MASK then
return (e % 2 ^ 4) // FLAGS_PAIR ~= 0
end
return false
>>>>>>> eae51988a9e3ca45e39ebcfdbea0f9f8706bd3cd
end end
-- HIGH 24 bits LOW 24 bits -- HIGH 24 bits LOW 24 bits
local function ECS_GENERATION(e: i53) local function ECS_GENERATION(e: i53): i24
<<<<<<< HEAD return if e > ECS_ENTITY_MASK then (e // 0x10) % ECS_GENERATION_MASK else 0
return (e // 0x10) % ECS_GENERATION_MASK
end
local function ECS_ID(e: i53)
return (e // 0x10) // ECS_ENTITY_MASK
=======
if e > ECS_ENTITY_MASK then
e = e // 0x10
return e % ECS_GENERATION_MASK
end
return 0
>>>>>>> eae51988a9e3ca45e39ebcfdbea0f9f8706bd3cd
end end
local function ECS_GENERATION_INC(e: i53) local function ECS_GENERATION_INC(e: i53)
@ -144,74 +113,28 @@ local function ECS_GENERATION_INC(e: i53)
local id = flags // ECS_ENTITY_MASK local id = flags // ECS_ENTITY_MASK
local generation = flags % ECS_GENERATION_MASK local generation = flags % ECS_GENERATION_MASK
<<<<<<< HEAD
return newId(id, generation + 1) + flags
end
-- gets the high ID
local function ECS_PAIR_FIRST(entity: i53): i24
return (entity // 0x10) % ECS_ENTITY_MASK
end
-- gets the low ID
local ECS_PAIR_SECOND = ECS_ID
local function ECS_PAIR(first: number, second: number):
local target = WILDCARD
local relation
if first == WILDCARD then
relation = second
elseif second == WILDCARD then
relation = first
else
relation = second
target = ECS_PAIR_SECOND(first)
end
return newId(
ECS_PAIR_SECOND(relation), target) + addFlags(--[[isPair]] true)
)
end
local function getAlive(entityIndex: EntityIndex, id: i53)
return entityIndex.dense[id]
=======
return ECS_COMBINE(id, generation + 1) + flags return ECS_COMBINE(id, generation + 1) + flags
else
return ECS_COMBINE(e, 1)
end end
return ECS_COMBINE(e, 1)
>>>>>>> eae51988a9e3ca45e39ebcfdbea0f9f8706bd3cd
end end
-- FIRST gets the high ID -- FIRST gets the high ID
local function ECS_ENTITY_T_HI(e: i53): i24 local function ECS_ENTITY_T_HI(e: i53): i24
if e > ECS_ENTITY_MASK then return if e > ECS_ENTITY_MASK then (e // 0x10) % ECS_ENTITY_MASK else e
e = e // 0x10
return e % ECS_ENTITY_MASK
end
return e
end end
<<<<<<< HEAD
local function nextEntityId(entityIndex, index: i24): i53
local id = newId(index, 0)
=======
-- SECOND -- SECOND
local function ECS_ENTITY_T_LO(e: i53): i24 local function ECS_ENTITY_T_LO(e: i53): i24
if e > ECS_ENTITY_MASK then return if e > ECS_ENTITY_MASK then (e // 0x10) // ECS_ENTITY_MASK else e
e = e // 0x10
return e // ECS_ENTITY_MASK
end
return e
end end
local function ECS_PAIR(pred: i53, obj: i53): i53 local function ECS_PAIR(pred: i53, obj: i53): i53
return ECS_COMBINE(ECS_ENTITY_T_LO(obj), ECS_ENTITY_T_LO(pred)) + addFlags(--[[isPair]] true) :: i53 return ECS_COMBINE(ECS_ENTITY_T_LO(obj), ECS_ENTITY_T_LO(pred)) + addFlags(--[[isPair]] true) :: i53
end end
local function getAlive(entityIndex: EntityIndex, id: i24) local function getAlive(entityIndex: EntityIndex, id: i24): i53
local entityId = entityIndex.dense[id] return entityIndex.dense[id]
return entityId
end end
-- ECS_PAIR_FIRST, gets the relationship target / obj / HIGH bits -- ECS_PAIR_FIRST, gets the relationship target / obj / HIGH bits
@ -227,7 +150,6 @@ end
local function nextEntityId(entityIndex: EntityIndex, index: i24): i53 local function nextEntityId(entityIndex: EntityIndex, index: i24): i53
--local id = ECS_COMBINE(index, 0) --local id = ECS_COMBINE(index, 0)
local id = index local id = index
>>>>>>> eae51988a9e3ca45e39ebcfdbea0f9f8706bd3cd
entityIndex.sparse[id] = { entityIndex.sparse[id] = {
dense = index, dense = index,
} :: Record } :: Record
@ -298,7 +220,7 @@ local function archetypeAppend(entity: number, archetype: Archetype): number
return length return length
end end
local function newEntity(entityId: i53, record: Record, archetype: Archetype) local function newEntity(entityId: i53, record: Record, archetype: Archetype): Record
local row = archetypeAppend(entityId, archetype) local row = archetypeAppend(entityId, archetype)
record.archetype = archetype record.archetype = archetype
record.row = row record.row = row
@ -314,7 +236,7 @@ local function moveEntity(entityIndex: EntityIndex, entityId: i53, record: Recor
record.row = destinationRow record.row = destinationRow
end end
local function hash(arr): string local function hash(arr: { number }): string
return table.concat(arr, "_") return table.concat(arr, "_")
end end
@ -327,7 +249,7 @@ local function ensureComponentRecord(
local archetypesMap = componentIndex[componentId] local archetypesMap = componentIndex[componentId]
if not archetypesMap then if not archetypesMap then
archetypesMap = ({ size = 0, cache = {} } :: any) :: ArchetypeMap archetypesMap = ({ size = 0, cache = {} } :: any) :: ArchetypeMap
componentIndex[componentId] = archetypesMap componentIndex[componentId] = archetypesMap
end end
@ -337,7 +259,7 @@ local function ensureComponentRecord(
return archetypesMap return archetypesMap
end end
local function ECS_ID_IS_WILDCARD(e) local function ECS_ID_IS_WILDCARD(e: i53): boolean
assert(ECS_IS_PAIR(e)) assert(ECS_IS_PAIR(e))
local first = ECS_ENTITY_T_HI(e) local first = ECS_ENTITY_T_HI(e)
local second = ECS_ENTITY_T_LO(e) local second = ECS_ENTITY_T_LO(e)
@ -391,7 +313,8 @@ end
local World = {} local World = {}
World.__index = World World.__index = World
function World.new()
function World.new(): World
local self = setmetatable({ local self = setmetatable({
archetypeIndex = {} :: { [string]: Archetype }, archetypeIndex = {} :: { [string]: Archetype },
archetypes = {} :: Archetypes, archetypes = {} :: Archetypes,
@ -409,12 +332,13 @@ function World.new()
ROOT_ARCHETYPE = (nil :: any) :: Archetype, ROOT_ARCHETYPE = (nil :: any) :: Archetype,
}, World) }, World)
self.ROOT_ARCHETYPE = archetypeOf(self, {}) self.ROOT_ARCHETYPE = archetypeOf(self, {})
return self
return table.freeze(self)
end end
export type World = typeof(World.new()) export type World = typeof(World.new())
function World.component(world: World) function World.component(world: World): i53
local componentId = world.nextComponentId + 1 local componentId = world.nextComponentId + 1
if componentId > HI_COMPONENT_ID then if componentId > HI_COMPONENT_ID then
-- IDs are partitioned into ranges because component IDs are not nominal, -- IDs are partitioned into ranges because component IDs are not nominal,
@ -425,7 +349,7 @@ function World.component(world: World)
return nextEntityId(world.entityIndex, componentId) return nextEntityId(world.entityIndex, componentId)
end end
function World.entity(world: World) function World.entity(world: World): i53
local entityId = world.nextEntityId + 1 local entityId = world.nextEntityId + 1
world.nextEntityId = entityId world.nextEntityId = entityId
return nextEntityId(world.entityIndex, entityId + REST) return nextEntityId(world.entityIndex, entityId + REST)
@ -473,6 +397,7 @@ local function archetypeDelete(world: World, id: i53)
local componentIndex = world.componentIndex local componentIndex = world.componentIndex
local archetypesMap = componentIndex[id] local archetypesMap = componentIndex[id]
local archetypes = world.archetypes local archetypes = world.archetypes
if archetypesMap then if archetypesMap then
for archetypeId in archetypesMap.cache do for archetypeId in archetypesMap.cache do
for _, entity in archetypes[archetypeId].entities do for _, entity in archetypes[archetypeId].entities do
@ -534,7 +459,7 @@ local function ensureArchetype(world: World, types, prev): Archetype
return archetypeOf(world, types, prev) return archetypeOf(world, types, prev)
end end
local function findInsert(types: { i53 }, toAdd: i53) local function findInsert(types: { i53 }, toAdd: i53): number
for i, id in types do for i, id in types do
if id == toAdd then if id == toAdd then
return -1 return -1
@ -546,7 +471,7 @@ local function findInsert(types: { i53 }, toAdd: i53)
return #types + 1 return #types + 1
end end
local function findArchetypeWith(world: World, node: Archetype, componentId: i53) local function findArchetypeWith(world: World, node: Archetype, componentId: i53): Archetype
local types = node.types local types = node.types
-- Component IDs are added incrementally, so inserting and sorting -- Component IDs are added incrementally, so inserting and sorting
-- them each time would be expensive. Instead this insertion sort can find the insertion -- them each time would be expensive. Instead this insertion sort can find the insertion
@ -564,7 +489,7 @@ local function findArchetypeWith(world: World, node: Archetype, componentId: i53
return ensureArchetype(world, destinationType, node) return ensureArchetype(world, destinationType, node)
end end
local function ensureEdge(archetype: Archetype, componentId: i53) local function ensureEdge(archetype: Archetype, componentId: i53): ArchetypeEdge
local edges = archetype.edges local edges = archetype.edges
local edge = edges[componentId] local edge = edges[componentId]
if not edge then if not edge then
@ -662,7 +587,7 @@ function World.remove(world: World, entityId: i53, componentId: i53)
end end
-- Keeping the function as small as possible to enable inlining -- Keeping the function as small as possible to enable inlining
local function get(record: Record, componentId: i24) local function get(record: Record, componentId: i24): any
local archetype = record.archetype local archetype = record.archetype
if not archetype then if not archetype then
return nil return nil
@ -677,7 +602,7 @@ local function get(record: Record, componentId: i24)
return archetype.columns[archetypeRecord][record.row] return archetype.columns[archetypeRecord][record.row]
end end
function World.get(world: World, entityId: i53, a: i53, b: i53?, c: i53?, d: i53?, e: i53?): any function World.get(world: World, entityId: i53, a: i53, b: i53?, c: i53?, d: i53?, e: i53?): ...any
local id = entityId local id = entityId
local record = world.entityIndex.sparse[id] local record = world.entityIndex.sparse[id]
if not record then if not record then
@ -716,10 +641,10 @@ export type Query = typeof(EmptyQuery)
type CompatibleArchetype = { archetype: Archetype, indices: { number } } type CompatibleArchetype = { archetype: Archetype, indices: { number } }
function World.query(world: World, ...): Query function World.query(world: World, ...: i53): Query
-- breaking? -- breaking?
if (...) == nil then if (...) == nil then
error("Missing components") error(" Missing components")
end end
local compatibleArchetypes: { CompatibleArchetype } = {} local compatibleArchetypes: { CompatibleArchetype } = {}
@ -772,7 +697,7 @@ function World.query(world: World, ...): Query
end end
local lastArchetype = 1 local lastArchetype = 1
local compatibleArchetype: CompatibleArchetype = compatibleArchetypes[lastArchetype] local compatibleArchetype: CompatibleArchetype = compatibleArchetypes[lastArchetype]
if not compatibleArchetype then if not compatibleArchetype then
return EmptyQuery return EmptyQuery
@ -785,7 +710,7 @@ function World.query(world: World, ...): Query
local i = 1 local i = 1
local function queryNext() local function queryNext(): ...any
local archetype = compatibleArchetype.archetype local archetype = compatibleArchetype.archetype
local entityId = archetype.entities[i] local entityId = archetype.entities[i]
@ -801,7 +726,7 @@ function World.query(world: World, ...): Query
end end
local row = i local row = i
i+=1 i += 1
local columns = archetype.columns local columns = archetype.columns
local tr = compatibleArchetype.indices local tr = compatibleArchetype.indices
@ -857,15 +782,15 @@ function World.query(world: World, ...): Query
return entityId, unpack(queryOutput :: any, 1, queryLength) return entityId, unpack(queryOutput :: any, 1, queryLength)
end end
function preparedQuery:__iter() function preparedQuery:__iter(): () -> ...any
return queryNext return queryNext
end end
function preparedQuery:next() function preparedQuery:next(): ...any
return queryNext() return queryNext()
end end
function preparedQuery:without(...) function preparedQuery:without(...: i53): Query
local withoutComponents = { ... } local withoutComponents = { ... }
for i = #compatibleArchetypes, 1, -1 do for i = #compatibleArchetypes, 1, -1 do
local archetype = compatibleArchetypes[i].archetype local archetype = compatibleArchetypes[i].archetype
@ -895,40 +820,53 @@ function World.query(world: World, ...): Query
return setmetatable({}, preparedQuery) :: any return setmetatable({}, preparedQuery) :: any
end end
function World.__iter(world: World): () -> any type WorldIterator = (() -> (i53, { [unknown]: unknown? })) & (() -> ()) & (() -> i53)
function World.__iter(world: World): WorldIterator
local dense = world.entityIndex.dense local dense = world.entityIndex.dense
local sparse = world.entityIndex.sparse local sparse = world.entityIndex.sparse
local last local last
return function() -- new solver doesnt like the world iterator type even tho its correct
local lastEntity: number?, entityId: number = next(dense, last) -- so any cast here i come
if not lastEntity then local iterator: WorldIterator = (
return function()
local lastEntity: number?, entityId: number = next(dense, last)
if not lastEntity then
-- ignore type error
return
end
last = lastEntity
local record = sparse[entityId]
local archetype = record.archetype
if not archetype then
-- Returns only the entity id as an entity without data should not return
-- data and allow the user to get an error if they don't handle the case.
return entityId
end
local row = record.row
local types = archetype.types
local columns = archetype.columns
local entityData = {}
for i, column in columns do
-- We use types because the key should be the component ID not the column index
entityData[types[i]] = column[row]
end
return entityId, entityData
end end
) :: any
last = lastEntity return iterator
local record = sparse[entityId]
local archetype = record.archetype
if not archetype then
-- Returns only the entity id as an entity without data should not return
-- data and allow the user to get an error if they don't handle the case.
return entityId
end
local row = record.row
local types = archetype.types
local columns = archetype.columns
local entityData = {}
for i, column in columns do
-- We use types because the key should be the component ID not the column index
entityData[types[i]] = column[row]
end
return entityId, entityData
end
end end
-- freezing it incase somebody trys doing something stupid and modifying it
-- (unlikely but its easy to add extra safety so)
table.freeze(World)
-- __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 & { __nominal_type_dont_use: T }