Compare commits

..

3 commits

Author SHA1 Message Date
Ukendio
78fe5338cf index component record after archetype gets created
Some checks are pending
analysis / Run Luau Analyze (push) Waiting to run
deploy-docs / build (push) Waiting to run
deploy-docs / Deploy (push) Blocked by required conditions
publish-npm / publish (push) Waiting to run
unit-testing / Run Luau Tests (push) Waiting to run
2025-07-18 16:53:32 +02:00
Ukendio
ca0689c92b Bump 2025-07-18 15:14:39 +02:00
Ukendio
117a5e0ca7 Fix on_remove hook not called on cached edge 2025-07-18 15:13:59 +02:00
4 changed files with 91 additions and 43 deletions

108
jecs.luau
View file

@ -2320,13 +2320,43 @@ local function world_new()
local to: Archetype local to: Archetype
local idr: ComponentRecord local idr: ComponentRecord
if ECS_IS_PAIR(id::number) then if ECS_IS_PAIR(id::number) then
local first = ECS_PAIR_FIRST(id::number)
local wc = ECS_PAIR(first, EcsWildcard)
idr = component_index[wc]
local edge = archetype_edges[src.id] local edge = archetype_edges[src.id]
to = edge[id] to = edge[id]
if not to then if to == nil then
local first = ECS_PAIR_FIRST(id::number) if idr and (bit32.btest(idr.flags) == true) then
local wc = ECS_PAIR(first, EcsWildcard) local cr = idr.records[src.id]
idr = component_index[wc] if cr then
if idr and bit32.btest(idr.flags, ECS_ID_IS_EXCLUSIVE) then local on_remove = idr.on_remove
local id_types = src.types
if on_remove then
on_remove(entity, id_types[cr])
src = record.archetype
id_types = src.types
cr = idr.records[src.id]
end
local dst = table.clone(id_types)
dst[cr] = id
to = archetype_ensure(world, dst)
end
end
if not to then
to = find_archetype_with(world, id, src)
if not idr then
idr = component_index[wc]
end
end
edge[id] = to
archetype_edges[(to :: Archetype).id][id] = src
else
if bit32.btest(idr.flags, ECS_ID_IS_EXCLUSIVE) then
local cr = idr.records[src.id] local cr = idr.records[src.id]
if cr then if cr then
local on_remove = idr.on_remove local on_remove = idr.on_remove
@ -2340,18 +2370,11 @@ local function world_new()
local dst = table.clone(id_types) local dst = table.clone(id_types)
dst[cr] = id dst[cr] = id
to = archetype_ensure(world, dst) to = archetype_ensure(world, dst)
else
to = find_archetype_with(world, id, src)
idr = component_index[id]
end end
else
to = find_archetype_with(world, id, src)
idr = component_index[id]
end end
edge[id] = to if not to then
archetype_edges[to.id][id] = src to = find_archetype_with(world, id, src)
else end
idr = component_index[id]
end end
else else
local edges = archetype_edges local edges = archetype_edges
@ -2401,14 +2424,45 @@ local function world_new()
end end
local to: Archetype local to: Archetype
local idr: ComponentRecord local idr: ComponentRecord
if ECS_IS_PAIR(id::number) then if ECS_IS_PAIR(id::number) then
local first = ECS_PAIR_FIRST(id::number)
local wc = ECS_PAIR(first, EcsWildcard)
idr = component_index[wc]
local edge = archetype_edges[src.id] local edge = archetype_edges[src.id]
to = edge[id] to = edge[id]
if not to then if to == nil then
local first = ECS_PAIR_FIRST(id::number) if idr and (bit32.btest(idr.flags) == true) then
local wc = ECS_PAIR(first, EcsWildcard) local cr = idr.records[src.id]
idr = component_index[wc] if cr then
if idr and bit32.btest(idr.flags, ECS_ID_IS_EXCLUSIVE) then local on_remove = idr.on_remove
local id_types = src.types
if on_remove then
on_remove(entity, id_types[cr])
src = record.archetype
id_types = src.types
cr = idr.records[src.id]
end
local dst = table.clone(id_types)
dst[cr] = id
to = archetype_ensure(world, dst)
end
end
if not to then
to = find_archetype_with(world, id, src)
if not idr then
idr = component_index[wc]
end
end
edge[id] = to
archetype_edges[(to :: Archetype).id][id] = src
else
if bit32.btest(idr.flags, ECS_ID_IS_EXCLUSIVE) then
local cr = idr.records[src.id] local cr = idr.records[src.id]
if cr then if cr then
local on_remove = idr.on_remove local on_remove = idr.on_remove
@ -2422,18 +2476,11 @@ local function world_new()
local dst = table.clone(id_types) local dst = table.clone(id_types)
dst[cr] = id dst[cr] = id
to = archetype_ensure(world, dst) to = archetype_ensure(world, dst)
else
to = find_archetype_with(world, id, src)
idr = component_index[id]
end end
else
to = find_archetype_with(world, id, src)
idr = component_index[id]
end end
edge[id] = to if not to then
archetype_edges[to.id][id] = src to = find_archetype_with(world, id, src)
else end
idr = component_index[id]
end end
else else
local edges = archetype_edges local edges = archetype_edges
@ -3104,6 +3151,7 @@ return {
ECS_GENERATION_INC = ECS_GENERATION_INC, ECS_GENERATION_INC = ECS_GENERATION_INC,
ECS_GENERATION = ECS_GENERATION, ECS_GENERATION = ECS_GENERATION,
ECS_ID_IS_WILDCARD = ECS_ID_IS_WILDCARD, ECS_ID_IS_WILDCARD = ECS_ID_IS_WILDCARD,
ECS_ID_IS_EXCLUSIVE = ECS_ID_IS_EXCLUSIVE,
ECS_ID_DELETE = ECS_ID_DELETE, ECS_ID_DELETE = ECS_ID_DELETE,
ECS_META_RESET = ECS_META_RESET, ECS_META_RESET = ECS_META_RESET,
ECS_COMBINE = ECS_COMBINE, ECS_COMBINE = ECS_COMBINE,

View file

@ -1,6 +1,6 @@
{ {
"name": "@rbxts/jecs", "name": "@rbxts/jecs",
"version": "0.9.0-rc.2", "version": "0.9.0-rc.3",
"description": "Stupidly fast Entity Component System", "description": "Stupidly fast Entity Component System",
"main": "jecs.luau", "main": "jecs.luau",
"repository": { "repository": {

View file

@ -346,23 +346,12 @@ TEST("world:add()", function()
world:add(A, jecs.Exclusive) world:add(A, jecs.Exclusive)
local on_remove_call = false local on_remove_call = false
world:set(A, jecs.OnRemove, function(e, id) world:set(A, jecs.OnRemove, function(e, id)
CHECK(e == e_ptr)
CHECK(id == jecs.pair(A, B))
on_remove_call = true on_remove_call = true
end) end)
local on_add_call_count = 0 local on_add_call_count = 0
world:set(A, jecs.OnAdd, function(e, id) world:set(A, jecs.OnAdd, function(e, id)
on_add_call_count += 1 on_add_call_count += 1
if on_add_call_count == 1 then
CHECK(e == e_ptr)
CHECK(id == jecs.pair(A, B))
elseif on_add_call_count == 2 then
CHECK(e == e_ptr)
CHECK(id == jecs.pair(A, C))
else
CHECK(false)
end
end) end)
@ -377,6 +366,17 @@ TEST("world:add()", function()
CHECK(world:has(e, pair(A, B)) == false) CHECK(world:has(e, pair(A, B)) == false)
CHECK(world:has(e, pair(A, C)) == true) CHECK(world:has(e, pair(A, C)) == true)
-- We have to ensure that it actually invokes hooks everytime it
-- traverses the archetype
e = world:entity()
world:add(e, pair(A, B))
CHECK(on_add_call_count == 3)
world:add(e, pair(A, C))
CHECK(on_add_call_count == 4)
CHECK(on_remove_call)
CHECK(world:has(e, pair(A, B)) == false)
CHECK(world:has(e, pair(A, C)) == true)
end end
do CASE "idempotent" do CASE "idempotent"

View file

@ -1,6 +1,6 @@
[package] [package]
name = "ukendio/jecs" name = "ukendio/jecs"
version = "0.9.0-rc.2" version = "0.9.0-rc.3"
registry = "https://github.com/UpliftGames/wally-index" registry = "https://github.com/UpliftGames/wally-index"
realm = "shared" realm = "shared"
license = "MIT" license = "MIT"