mirror of
https://github.com/Ukendio/jecs.git
synced 2025-04-25 01:20:04 +00:00
Add bit flags to id_record (#101)
* Add bit flags to id_record * set should not invoke OnAdd
This commit is contained in:
parent
33f359a150
commit
6d6cc37a25
1 changed files with 197 additions and 140 deletions
337
src/init.luau
337
src/init.luau
|
@ -41,6 +41,7 @@ type ArchetypeRecord = {
|
|||
|
||||
type ArchetypeMap = {
|
||||
cache: { ArchetypeRecord },
|
||||
flags: number,
|
||||
first: ArchetypeMap,
|
||||
second: ArchetypeMap,
|
||||
parent: ArchetypeMap,
|
||||
|
@ -58,6 +59,10 @@ type ArchetypeDiff = {
|
|||
|
||||
local HI_COMPONENT_ID = 256
|
||||
|
||||
--------------------------------------------------
|
||||
-------- ID_RECORD -------------------------------
|
||||
--------------------------------------------------
|
||||
|
||||
local EcsOnAdd = HI_COMPONENT_ID + 1
|
||||
local EcsOnRemove = HI_COMPONENT_ID + 2
|
||||
local EcsOnSet = HI_COMPONENT_ID + 3
|
||||
|
@ -72,6 +77,10 @@ local ECS_ID_FLAGS_MASK = 0x10
|
|||
local ECS_ENTITY_MASK = bit32.lshift(1, 24)
|
||||
local ECS_GENERATION_MASK = bit32.lshift(1, 16)
|
||||
|
||||
local ECS_ID_HAS_DELETE = 0b0001
|
||||
local ECS_ID_HAS_HOOKS = 0b0010
|
||||
--local EcsIdExclusive = 0b0100
|
||||
|
||||
local function FLAGS_ADD(is_pair: boolean): number
|
||||
local flags = 0x0
|
||||
|
||||
|
@ -257,115 +266,6 @@ local function hash(arr: { number }): string
|
|||
return table.concat(arr, "_")
|
||||
end
|
||||
|
||||
local function id_record_ensure(
|
||||
componentIndex: ComponentIndex,
|
||||
componentId: number
|
||||
): ArchetypeMap
|
||||
local idr = componentIndex[componentId]
|
||||
|
||||
if not idr then
|
||||
idr = ({ size = 0, cache = {} } :: any) :: ArchetypeMap
|
||||
componentIndex[componentId] = idr
|
||||
end
|
||||
|
||||
return idr
|
||||
end
|
||||
|
||||
local function ECS_ID_IS_WILDCARD(e: i53): boolean
|
||||
assert(ECS_IS_PAIR(e))
|
||||
local first = ECS_ENTITY_T_HI(e)
|
||||
local second = ECS_ENTITY_T_LO(e)
|
||||
return first == EcsWildcard or second == EcsWildcard
|
||||
end
|
||||
|
||||
local function archetype_create(world: any, types: { i24 }, prev: Archetype?): Archetype
|
||||
local ty = hash(types)
|
||||
|
||||
local id = world.nextArchetypeId + 1
|
||||
world.nextArchetypeId = id
|
||||
|
||||
local length = #types
|
||||
local columns = (table.create(length) :: any) :: { Column }
|
||||
local componentIndex = world.componentIndex
|
||||
|
||||
local records: { ArchetypeRecord } = {}
|
||||
for i, componentId in types do
|
||||
local tr = { column = i, count = 1 }
|
||||
local idr = id_record_ensure(componentIndex, componentId)
|
||||
idr.cache[id] = tr
|
||||
idr.size += 1
|
||||
records[componentId] = tr
|
||||
if ECS_IS_PAIR(componentId) then
|
||||
local relation = ecs_pair_first(world, componentId)
|
||||
local object = ecs_pair_second(world, componentId)
|
||||
|
||||
local r = ECS_PAIR(relation, EcsWildcard)
|
||||
local idr_r = id_record_ensure(componentIndex, r)
|
||||
|
||||
local o = ECS_PAIR(EcsWildcard, object)
|
||||
local idr_o = id_record_ensure(componentIndex, o)
|
||||
|
||||
records[r] = tr
|
||||
records[o] = tr
|
||||
|
||||
idr_r.cache[id] = tr
|
||||
idr_o.cache[id] = tr
|
||||
|
||||
idr_r.size += 1
|
||||
idr_o.size += 1
|
||||
end
|
||||
columns[i] = {}
|
||||
end
|
||||
|
||||
local archetype: Archetype = {
|
||||
columns = columns,
|
||||
edges = {},
|
||||
entities = {},
|
||||
id = id,
|
||||
records = records,
|
||||
type = ty,
|
||||
types = types,
|
||||
}
|
||||
|
||||
world.archetypeIndex[ty] = archetype
|
||||
world.archetypes[id] = archetype
|
||||
|
||||
return archetype
|
||||
end
|
||||
|
||||
local function world_entity(world: World): i53
|
||||
local entityId = world.nextEntityId + 1
|
||||
world.nextEntityId = entityId
|
||||
return entity_index_new_id(world.entityIndex, entityId + EcsRest)
|
||||
end
|
||||
|
||||
-- TODO:
|
||||
-- should have an additional `nth` parameter which selects the nth target
|
||||
-- this is important when an entity can have multiple relationships with the same target
|
||||
local function world_target(world: World, entity: i53, relation: i24--[[, nth: number]]): i24?
|
||||
local record = world.entityIndex.sparse[entity]
|
||||
local archetype = record.archetype
|
||||
if not archetype then
|
||||
return nil
|
||||
end
|
||||
|
||||
local idr = world.componentIndex[ECS_PAIR(relation, EcsWildcard)]
|
||||
if not idr then
|
||||
return nil
|
||||
end
|
||||
|
||||
local tr = idr.cache[archetype.id]
|
||||
if not tr then
|
||||
return nil
|
||||
end
|
||||
|
||||
return ecs_pair_second(world, archetype.types[tr.column])
|
||||
end
|
||||
|
||||
local function world_parent(world: World, entity: i53)
|
||||
return world_target(world, entity, EcsChildOf)
|
||||
end
|
||||
|
||||
local world_get: (world: World, entityId: i53, a: i53, b: i53?, c: i53?, d: i53?, e: i53?) -> (...any)
|
||||
do
|
||||
-- Keeping the function as small as possible to enable inlining
|
||||
|
@ -468,6 +368,158 @@ local function world_has(world: World, entity: number, ...: i53): boolean
|
|||
return true
|
||||
end
|
||||
|
||||
local function world_has_any(world: World, entity: number, ...: i53): boolean
|
||||
local record = world.entityIndex.sparse[entity]
|
||||
if not record then
|
||||
return false
|
||||
end
|
||||
|
||||
local archetype = record.archetype
|
||||
if not archetype then
|
||||
return false
|
||||
end
|
||||
|
||||
local records = archetype.records
|
||||
|
||||
for i = 1, select("#", ...) do
|
||||
if not records[select(i, ...)] then
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
|
||||
local function id_record_ensure(
|
||||
world,
|
||||
id: number
|
||||
): ArchetypeMap
|
||||
local componentIndex = world.componentIndex
|
||||
local idr = componentIndex[id]
|
||||
|
||||
if not idr then
|
||||
local flags = 0b0000
|
||||
local relation = ECS_ENTITY_T_HI(id)
|
||||
if world_has_one_inline(world, relation, EcsDelete) then
|
||||
flags = bit32.bor(flags, ECS_ID_HAS_DELETE)
|
||||
end
|
||||
|
||||
if world_has_any(world, relation,
|
||||
EcsOnAdd, EcsOnSet, EcsOnRemove)
|
||||
then
|
||||
flags = bit32.bor(flags, ECS_ID_HAS_HOOKS)
|
||||
end
|
||||
|
||||
-- local FLAG2 = 0b0010
|
||||
-- local FLAG3 = 0b0100
|
||||
-- local FLAG4 = 0b1000
|
||||
|
||||
idr = {
|
||||
size = 0,
|
||||
cache = {},
|
||||
flags = flags
|
||||
} :: ArchetypeMap
|
||||
componentIndex[id] = idr
|
||||
end
|
||||
|
||||
return idr
|
||||
end
|
||||
|
||||
local function ECS_ID_IS_WILDCARD(e: i53): boolean
|
||||
assert(ECS_IS_PAIR(e))
|
||||
local first = ECS_ENTITY_T_HI(e)
|
||||
local second = ECS_ENTITY_T_LO(e)
|
||||
return first == EcsWildcard or second == EcsWildcard
|
||||
end
|
||||
|
||||
local function archetype_create(world: any, types: { i24 }, prev: Archetype?): Archetype
|
||||
local ty = hash(types)
|
||||
|
||||
local id = world.nextArchetypeId + 1
|
||||
world.nextArchetypeId = id
|
||||
|
||||
local length = #types
|
||||
local columns = (table.create(length) :: any) :: { Column }
|
||||
|
||||
local records: { ArchetypeRecord } = {}
|
||||
for i, componentId in types do
|
||||
local tr = { column = i, count = 1 }
|
||||
local idr = id_record_ensure(world, componentId)
|
||||
idr.cache[id] = tr
|
||||
idr.size += 1
|
||||
records[componentId] = tr
|
||||
if ECS_IS_PAIR(componentId) then
|
||||
local relation = ecs_pair_first(world, componentId)
|
||||
local object = ecs_pair_second(world, componentId)
|
||||
|
||||
local r = ECS_PAIR(relation, EcsWildcard)
|
||||
local idr_r = id_record_ensure(world, r)
|
||||
|
||||
local o = ECS_PAIR(EcsWildcard, object)
|
||||
local idr_o = id_record_ensure(world, o)
|
||||
|
||||
records[r] = tr
|
||||
records[o] = tr
|
||||
|
||||
idr_r.cache[id] = tr
|
||||
idr_o.cache[id] = tr
|
||||
|
||||
idr_r.size += 1
|
||||
idr_o.size += 1
|
||||
end
|
||||
columns[i] = {}
|
||||
end
|
||||
|
||||
local archetype: Archetype = {
|
||||
columns = columns,
|
||||
edges = {},
|
||||
entities = {},
|
||||
id = id,
|
||||
records = records,
|
||||
type = ty,
|
||||
types = types,
|
||||
}
|
||||
|
||||
world.archetypeIndex[ty] = archetype
|
||||
world.archetypes[id] = archetype
|
||||
|
||||
return archetype
|
||||
end
|
||||
|
||||
local function world_entity(world: World): i53
|
||||
local entityId = world.nextEntityId + 1
|
||||
world.nextEntityId = entityId
|
||||
return entity_index_new_id(world.entityIndex, entityId + EcsRest)
|
||||
end
|
||||
|
||||
-- TODO:
|
||||
-- should have an additional `nth` parameter which selects the nth target
|
||||
-- this is important when an entity can have multiple relationships with the same target
|
||||
local function world_target(world: World, entity: i53, relation: i24--[[, nth: number]]): i24?
|
||||
local record = world.entityIndex.sparse[entity]
|
||||
local archetype = record.archetype
|
||||
if not archetype then
|
||||
return nil
|
||||
end
|
||||
|
||||
local idr = world.componentIndex[ECS_PAIR(relation, EcsWildcard)]
|
||||
if not idr then
|
||||
return nil
|
||||
end
|
||||
|
||||
local tr = idr.cache[archetype.id]
|
||||
if not tr then
|
||||
return nil
|
||||
end
|
||||
|
||||
return ecs_pair_second(world, archetype.types[tr.column])
|
||||
end
|
||||
|
||||
local function world_parent(world: World, entity: i53)
|
||||
return world_target(world, entity, EcsChildOf)
|
||||
end
|
||||
|
||||
local function archetype_ensure(world: World, types, prev): Archetype
|
||||
if #types < 1 then
|
||||
return world.ROOT_ARCHETYPE
|
||||
|
@ -560,7 +612,12 @@ local function world_add(world: World, entity: i53, id: i53)
|
|||
end
|
||||
end
|
||||
|
||||
invoke_hook(world, EcsOnAdd, id, entity)
|
||||
local idr = world.componentIndex[id]
|
||||
local has_hooks = bit32.band(idr.flags, ECS_ID_HAS_HOOKS) ~= 0
|
||||
|
||||
if has_hooks then
|
||||
invoke_hook(world, EcsOnAdd, id, entity)
|
||||
end
|
||||
end
|
||||
|
||||
-- Symmetric like `World.add` but idempotent
|
||||
|
@ -568,13 +625,18 @@ local function world_set(world: World, entity: i53, id: i53, data: unknown)
|
|||
local record = world.entityIndex.sparse[entity]
|
||||
local from = record.archetype
|
||||
local to = archetype_traverse_add(world, id, from)
|
||||
local idr = world.componentIndex[id]
|
||||
local has_hooks = bit32.band(idr.flags, ECS_ID_HAS_HOOKS) ~= 0
|
||||
|
||||
if from == to then
|
||||
if from == to then
|
||||
-- If the archetypes are the same it can avoid moving the entity
|
||||
-- and just set the data directly.
|
||||
local tr = to.records[id]
|
||||
from.columns[tr.column][record.row] = data
|
||||
invoke_hook(world, EcsOnSet, id, entity, data)
|
||||
if has_hooks then
|
||||
invoke_hook(world, EcsOnSet, id, entity, data)
|
||||
end
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
|
@ -588,10 +650,12 @@ local function world_set(world: World, entity: i53, id: i53, data: unknown)
|
|||
end
|
||||
end
|
||||
|
||||
local tr = to.records[id]
|
||||
local tr = to.records[id]
|
||||
to.columns[tr.column][record.row] = data
|
||||
|
||||
invoke_hook(world, EcsOnSet, id, entity, data)
|
||||
if has_hooks then
|
||||
invoke_hook(world, EcsOnSet, id, entity, data)
|
||||
end
|
||||
end
|
||||
|
||||
local function world_component(world: World): i53
|
||||
|
@ -607,7 +671,6 @@ local function world_component(world: World): i53
|
|||
return id
|
||||
end
|
||||
|
||||
|
||||
local function archetype_traverse_remove(world: World, id: i53, from: Archetype): Archetype
|
||||
local edge = edge_ensure(from, id)
|
||||
|
||||
|
@ -658,20 +721,6 @@ local function world_clear(world: World, entity: i53)
|
|||
entity_move(world.entityIndex, entity, record, ROOT_ARCHETYPE)
|
||||
end
|
||||
|
||||
-- should reuse this logic in World.set instead of swap removing in transition archetype
|
||||
local function columns_destruct(columns: { Column }, count: number, row: number)
|
||||
if row == count then
|
||||
for _, column in columns do
|
||||
column[count] = nil
|
||||
end
|
||||
else
|
||||
for _, column in columns do
|
||||
column[row] = column[count]
|
||||
column[count] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function archetype_fast_delete_last(world, columns,
|
||||
column_count, types, entity)
|
||||
|
||||
|
@ -731,7 +780,6 @@ local function archetype_delete(world: World, archetype,
|
|||
|
||||
local idr = component_index[delete]
|
||||
if idr then
|
||||
component_index[delete] = nil
|
||||
local children = {}
|
||||
for archetype_id in idr.cache do
|
||||
local idr_archetype = archetypes[archetype_id]
|
||||
|
@ -740,13 +788,19 @@ local function archetype_delete(world: World, archetype,
|
|||
table.insert(children, child)
|
||||
end
|
||||
end
|
||||
for _, child in children do
|
||||
if world_has_one_inline(world, child, EcsDelete) then
|
||||
local flags = idr.flags
|
||||
if bit32.band(flags, ECS_ID_HAS_DELETE) ~= 0 then
|
||||
for _, child in children do
|
||||
-- Cascade deletion to children
|
||||
world_delete(world, child)
|
||||
else
|
||||
end
|
||||
else
|
||||
for _, child in children do
|
||||
world_remove(world, child, delete)
|
||||
end
|
||||
end
|
||||
|
||||
component_index[delete] = nil
|
||||
end
|
||||
|
||||
-- TODO: iterate each linked record.
|
||||
|
@ -793,24 +847,27 @@ local function archetype_delete(world: World, archetype,
|
|||
continue
|
||||
end
|
||||
|
||||
local relation = ECS_ENTITY_T_HI(id)
|
||||
local id_record = component_index[id]
|
||||
|
||||
if world_has_one_inline(world, relation, EcsDelete) then
|
||||
for _, child in children do
|
||||
-- Cascade deletions of it has Delete as component trait
|
||||
world_delete(world, child)
|
||||
if id_record then
|
||||
local flags = id_record.flags
|
||||
if bit32.band(flags, ECS_ID_HAS_DELETE) ~= 0 then
|
||||
for _, child in children do
|
||||
-- Cascade deletions of it has Delete as component trait
|
||||
world_delete(world, child)
|
||||
end
|
||||
end
|
||||
else
|
||||
local object = ECS_ENTITY_T_LO(id)
|
||||
if object == delete then
|
||||
local p = ECS_PAIR(relation, object)
|
||||
for _, child in children do
|
||||
world_remove(world, child, p)
|
||||
world_remove(world, child, id)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
component_index[o] = nil
|
||||
end
|
||||
end
|
||||
|
||||
|
|
Loading…
Reference in a new issue