mirror of
https://github.com/Ukendio/jecs.git
synced 2025-07-06 14:49:17 +00:00
Cleanup functions and add missing types
This commit is contained in:
parent
ca8e1376ca
commit
447c7f12e7
1 changed files with 88 additions and 150 deletions
168
lib/init.lua
168
lib/init.lua
|
@ -11,14 +11,14 @@ type ArchetypeId = number
|
||||||
|
|
||||||
type Column = { any }
|
type Column = { any }
|
||||||
|
|
||||||
type Archetype = {
|
type ArchetypeEdge = {
|
||||||
id: number,
|
|
||||||
edges: {
|
|
||||||
[i53]: {
|
|
||||||
add: Archetype,
|
add: Archetype,
|
||||||
remove: Archetype,
|
remove: Archetype,
|
||||||
},
|
}
|
||||||
},
|
|
||||||
|
type Archetype = {
|
||||||
|
id: number,
|
||||||
|
edges: { [i53]: ArchetypeEdge },
|
||||||
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
|
||||||
end
|
else
|
||||||
return ECS_COMBINE(e, 1)
|
return ECS_COMBINE(e, 1)
|
||||||
>>>>>>> eae51988a9e3ca45e39ebcfdbea0f9f8706bd3cd
|
end
|
||||||
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
|
||||||
|
|
||||||
|
@ -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,7 +641,7 @@ 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")
|
||||||
|
@ -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]
|
||||||
|
|
||||||
|
@ -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,14 +820,20 @@ 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
|
||||||
|
-- so any cast here i come
|
||||||
|
local iterator: WorldIterator = (
|
||||||
|
function()
|
||||||
local lastEntity: number?, entityId: number = next(dense, last)
|
local lastEntity: number?, entityId: number = next(dense, last)
|
||||||
if not lastEntity then
|
if not lastEntity then
|
||||||
|
-- ignore type error
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -927,8 +858,15 @@ function World.__iter(world: World): () -> any
|
||||||
|
|
||||||
return entityId, entityData
|
return entityId, entityData
|
||||||
end
|
end
|
||||||
|
) :: any
|
||||||
|
|
||||||
|
return iterator
|
||||||
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 }
|
||||||
|
|
Loading…
Reference in a new issue