Skip to content

Commit

Permalink
Add EcsTag (#108)
Browse files Browse the repository at this point in the history
* Add EcsTag

* Add tests
  • Loading branch information
Ukendio authored Aug 28, 2024
1 parent ff98e8b commit 3dd0bd3
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 7 deletions.
1 change: 1 addition & 0 deletions .luaurc
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@
"aliases": {
"jecs": "src",
"testkit": "testkit",
"mirror": "mirror",
}
}
52 changes: 46 additions & 6 deletions src/init.luau
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,8 @@ local EcsChildOf = HI_COMPONENT_ID + 5
local EcsComponent = HI_COMPONENT_ID + 6
local EcsOnDeleteTarget = HI_COMPONENT_ID + 7
local EcsDelete = HI_COMPONENT_ID + 8
local EcsRest = HI_COMPONENT_ID + 9
local EcsTag = HI_COMPONENT_ID + 9
local EcsRest = HI_COMPONENT_ID + 10

local ECS_PAIR_FLAG = 0x8
local ECS_ID_FLAGS_MASK = 0x10
Expand All @@ -77,6 +78,9 @@ local ECS_GENERATION_MASK = bit32.lshift(1, 16)
local ECS_ID_HAS_DELETE = 0b0001
local ECS_ID_HAS_HOOKS = 0b0010
--local EcsIdExclusive = 0b0100
local ECS_ID_IS_TAG = 0b1000

local NULL_ARRAY = table.freeze({})

local function FLAGS_ADD(is_pair: boolean): number
local flags = 0x0
Expand Down Expand Up @@ -197,13 +201,16 @@ local function archetype_move(entity_index: EntityIndex, to: Archetype,
local records = to.records

for i, column in src_columns do
-- Retrieves the new column index from the source archetype's record from each component
if column == NULL_ARRAY then
continue
end
-- Retrieves the new column index from the source archetype's record from each component
-- We have to do this because the columns are tightly packed and indexes may not correspond to each other.
local tr = records[types[i]]

-- Sometimes target column may not exist, e.g. when you remove a component.
if tr then
dst_columns[tr.column][dst_row] = column[src_row]
dst_columns[tr.column][dst_row] = column[src_row]
end
-- If the entity is the last row in the archetype then swapping it would be meaningless.
if src_row ~= last then
Expand Down Expand Up @@ -329,6 +336,22 @@ local function world_get_one_inline(world: World, entity: i53, id: i53)
return archetype.columns[tr.column][record.row]
end

local function world_has_one_inline(world: World, entity: number, id: 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

return records[id] ~= nil
end

local function world_has(world: World, entity: number, ...: i53): boolean
local record = world.entityIndex.sparse[entity]
if not record then
Expand Down Expand Up @@ -417,6 +440,10 @@ local function id_record_ensure(
flags = bit32.bor(flags, ECS_ID_HAS_HOOKS)
end

if world_has_one_inline(world, id, EcsTag) then
flags = bit32.bor(flags, ECS_ID_IS_TAG)
end

-- local FLAG2 = 0b0010
-- local FLAG3 = 0b0100
-- local FLAG4 = 0b1000
Expand All @@ -432,7 +459,7 @@ local function id_record_ensure(
return idr
end

local function _ECS_ID_IS_WILDCARD(e: i53): boolean
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)
Expand Down Expand Up @@ -474,7 +501,11 @@ local function archetype_create(world: World, types: { i24 }, prev: Archetype?):
idr_r.size += 1
idr_o.size += 1
end
columns[i] = {}
if bit32.band(idr.flags, ECS_ID_IS_TAG) == 0 then
columns[i] = {}
else
columns[i] = NULL_ARRAY
end
end

local archetype: Archetype = {
Expand Down Expand Up @@ -610,9 +641,14 @@ local function world_set(world: World, entity: i53, id: i53, data: unknown)
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
local flags = idr.flags
local is_tag = bit32.band(flags, ECS_ID_IS_TAG) ~= 0
local has_hooks = bit32.band(flags, ECS_ID_HAS_HOOKS) ~= 0

if from == to then
if is_tag then
return
end
-- If the archetypes are the same it can avoid moving the entity
-- and just set the data directly.
local tr = to.records[id]
Expand All @@ -637,6 +673,9 @@ local function world_set(world: World, entity: i53, id: i53, data: unknown)
local tr = to.records[id]
local column = to.columns[tr.column]

if is_tag then
return
end
if not has_hooks then
column[record.row] = data
else
Expand Down Expand Up @@ -1558,6 +1597,7 @@ return {
w = EcsWildcard :: Entity,
OnDeleteTarget = EcsOnDeleteTarget :: Entity,
Delete = EcsDelete :: Entity,
Tag = EcsTag :: Entity,
Rest = EcsRest :: Entity,

pair = (ECS_PAIR :: any) :: <R, T>(pred: Entity, obj: Entity) -> number,
Expand Down
29 changes: 28 additions & 1 deletion test/tests.luau
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,16 @@ TEST("world:add()", function()
end)

TEST("world:query()", function()
do CASE "tag"
local world = jecs.World.new()
local A = world:component()
world:add(A, jecs.Tag)
local e = world:entity()
world:set(e, A, "test")
for id, a in world:query(A) do
CHECK(a == nil)
end
end
do CASE "query single component"
do
local world = jecs.World.new()
Expand Down Expand Up @@ -724,6 +734,24 @@ TEST("world:component()", function()
CHECK(world:has(A, jecs.Component))
CHECK(not world:has(e, jecs.Component))
end

do CASE "tag"
local world = jecs.World.new() :: World
local A = world:component()
local B = world:component()
local C = world:component()
world:add(B, jecs.Tag)
world:add(C, jecs.Tag)
local e = world:entity()
world:set(e, A, "test")
world:add(e, B, "test")
world:set(e, C, 11)

CHECK(world:has(e, A))
CHECK(world:get(e, A) == "test")
CHECK(world:get(e, B) == nil)
CHECK(world:get(e, C) == nil)
end
end)

TEST("world:delete", function()
Expand Down Expand Up @@ -1297,7 +1325,6 @@ TEST("scheduler", function()
for _, system in events[RenderStepped] do
system.callback()
end
print(Heartbeat, events[Heartbeat])
for _, system in events[Heartbeat] do
system.callback()
end
Expand Down

0 comments on commit 3dd0bd3

Please sign in to comment.