Add component trait lazily
Some checks failed
analysis / Run Luau Analyze (push) Has been cancelled
deploy-docs / build (push) Has been cancelled
publish-npm / publish (push) Has been cancelled
unit-testing / Run Luau Tests (push) Has been cancelled
deploy-docs / Deploy (push) Has been cancelled

This commit is contained in:
Ukendio 2025-08-25 03:01:05 +02:00
parent 5de842d144
commit 29a66d92c2
3 changed files with 80 additions and 24 deletions

View file

@ -160,6 +160,10 @@ local function monitors_new<T...>(
end end
local function removed(entity: jecs.Entity, component: jecs.Id) local function removed(entity: jecs.Entity, component: jecs.Id)
local r = jecs.record(world, entity)
if not archetypes[r.archetype.id] then
return
end
local EcsOnRemove = jecs.OnRemove :: jecs.Id local EcsOnRemove = jecs.OnRemove :: jecs.Id
if callback ~= nil then if callback ~= nil then
callback(entity, EcsOnRemove) callback(entity, EcsOnRemove)

View file

@ -1078,18 +1078,6 @@ local function archetype_traverse_add(
return to return to
end end
local function world_component(world: world): i53
local id = (world.max_component_id :: number) + 1
if id > HI_COMPONENT_ID then
-- IDs are partitioned into ranges because component IDs are not nominal,
-- so it needs to error when IDs intersect into the entity range.
error("Too many components, consider using world:entity() instead to create components.")
end
world.max_component_id = id
return id
end
local function archetype_fast_delete_last(columns: { Column }, column_count: number) local function archetype_fast_delete_last(columns: { Column }, column_count: number)
for i, column in columns do for i, column in columns do
if column ~= NULL_ARRAY then if column ~= NULL_ARRAY then
@ -1327,6 +1315,9 @@ local function query_iter_init(query: QueryInner): () -> (number, ...any)
entities = archetype.entities entities = archetype.entities
i = #entities i = #entities
if i == 0 then
continue
end
entity = entities[i] entity = entities[i]
columns_map = archetype.columns_map columns_map = archetype.columns_map
a = columns_map[A] a = columns_map[A]
@ -1349,6 +1340,9 @@ local function query_iter_init(query: QueryInner): () -> (number, ...any)
entities = archetype.entities entities = archetype.entities
i = #entities i = #entities
if i == 0 then
continue
end
entity = entities[i] entity = entities[i]
columns_map = archetype.columns_map columns_map = archetype.columns_map
a = columns_map[A] a = columns_map[A]
@ -1372,6 +1366,9 @@ local function query_iter_init(query: QueryInner): () -> (number, ...any)
entities = archetype.entities entities = archetype.entities
i = #entities i = #entities
if i == 0 then
continue
end
entity = entities[i] entity = entities[i]
columns_map = archetype.columns_map columns_map = archetype.columns_map
a = columns_map[A] a = columns_map[A]
@ -1396,6 +1393,9 @@ local function query_iter_init(query: QueryInner): () -> (number, ...any)
entities = archetype.entities entities = archetype.entities
i = #entities i = #entities
if i == 0 then
continue
end
entity = entities[i] entity = entities[i]
columns_map = archetype.columns_map columns_map = archetype.columns_map
a = columns_map[A] a = columns_map[A]
@ -1421,6 +1421,9 @@ local function query_iter_init(query: QueryInner): () -> (number, ...any)
entities = archetype.entities entities = archetype.entities
i = #entities i = #entities
if i == 0 then
continue
end
entity = entities[i] entity = entities[i]
columns_map = archetype.columns_map columns_map = archetype.columns_map
a = columns_map[A] a = columns_map[A]
@ -1447,6 +1450,9 @@ local function query_iter_init(query: QueryInner): () -> (number, ...any)
entities = archetype.entities entities = archetype.entities
i = #entities i = #entities
if i == 0 then
continue
end
entity = entities[i] entity = entities[i]
columns_map = archetype.columns_map columns_map = archetype.columns_map
a = columns_map[A] a = columns_map[A]
@ -1474,6 +1480,9 @@ local function query_iter_init(query: QueryInner): () -> (number, ...any)
entities = archetype.entities entities = archetype.entities
i = #entities i = #entities
if i == 0 then
continue
end
entity = entities[i] entity = entities[i]
columns_map = archetype.columns_map columns_map = archetype.columns_map
a = columns_map[A] a = columns_map[A]
@ -1502,6 +1511,9 @@ local function query_iter_init(query: QueryInner): () -> (number, ...any)
entities = archetype.entities entities = archetype.entities
i = #entities i = #entities
if i == 0 then
continue
end
entity = entities[i] entity = entities[i]
columns_map = archetype.columns_map columns_map = archetype.columns_map
a = columns_map[A] a = columns_map[A]
@ -1533,6 +1545,9 @@ local function query_iter_init(query: QueryInner): () -> (number, ...any)
entities = archetype.entities entities = archetype.entities
i = #entities i = #entities
if i == 0 then
continue
end
entity = entities[i] entity = entities[i]
columns_map = archetype.columns_map columns_map = archetype.columns_map
a = columns_map[A] a = columns_map[A]
@ -1700,6 +1715,9 @@ local function query_cached(query: QueryInner)
entities = archetype.entities entities = archetype.entities
i = #entities i = #entities
if i == 0 then
continue
end
entity = entities[i] entity = entities[i]
columns_map = archetype.columns_map columns_map = archetype.columns_map
a = columns_map[A] a = columns_map[A]
@ -1722,6 +1740,9 @@ local function query_cached(query: QueryInner)
entities = archetype.entities entities = archetype.entities
i = #entities i = #entities
if i == 0 then
continue
end
entity = entities[i] entity = entities[i]
columns_map = archetype.columns_map columns_map = archetype.columns_map
a = columns_map[A] a = columns_map[A]
@ -1745,6 +1766,9 @@ local function query_cached(query: QueryInner)
entities = archetype.entities entities = archetype.entities
i = #entities i = #entities
if i == 0 then
continue
end
entity = entities[i] entity = entities[i]
columns_map = archetype.columns_map columns_map = archetype.columns_map
a = columns_map[A] a = columns_map[A]
@ -1769,6 +1793,9 @@ local function query_cached(query: QueryInner)
entities = archetype.entities entities = archetype.entities
i = #entities i = #entities
if i == 0 then
continue
end
entity = entities[i] entity = entities[i]
columns_map = archetype.columns_map columns_map = archetype.columns_map
a = columns_map[A] a = columns_map[A]
@ -1853,6 +1880,10 @@ local function query_cached(query: QueryInner)
entities = archetype.entities entities = archetype.entities
i = #entities i = #entities
if i == 0 then
continue
end
entity = entities[i]
columns_map = archetype.columns_map columns_map = archetype.columns_map
a = columns_map[A] a = columns_map[A]
b = columns_map[B] b = columns_map[B]
@ -1880,6 +1911,9 @@ local function query_cached(query: QueryInner)
entities = archetype.entities entities = archetype.entities
i = #entities i = #entities
if i == 0 then
continue
end
entity = entities[i] entity = entities[i]
columns_map = archetype.columns_map columns_map = archetype.columns_map
a = columns_map[A] a = columns_map[A]
@ -1911,6 +1945,9 @@ local function query_cached(query: QueryInner)
entities = archetype.entities entities = archetype.entities
i = #entities i = #entities
if i == 0 then
continue
end
entity = entities[i] entity = entities[i]
columns_map = archetype.columns_map columns_map = archetype.columns_map
a = columns_map[A] a = columns_map[A]
@ -2150,6 +2187,10 @@ local function world_new()
removed = {} :: Signal removed = {} :: Signal
} }
-- We need to cache the moment the world is registered, that way
-- `world:component` will not pollute the global registration of components.
local max_component_id = ecs_max_component_id
local world = { local world = {
archetype_edges = archetype_edges, archetype_edges = archetype_edges,
@ -3124,6 +3165,19 @@ local function world_new()
world.archetype_index = new_archetype_map world.archetype_index = new_archetype_map
end end
local function world_component(world: world): i53
max_component_id += 1
if max_component_id > HI_COMPONENT_ID then
-- IDs are partitioned into ranges because component IDs are not nominal,
-- so it needs to error when IDs intersect into the entity range.
error("Too many components, consider using world:entity() instead to create components.")
end
world.max_component_id = max_component_id
inner_world_add(world, max_component_id, EcsComponent)
return max_component_id
end
world.entity = inner_world_entity world.entity = inner_world_entity
world.query = world_query :: any world.query = world_query :: any
world.remove = inner_world_remove world.remove = inner_world_remove
@ -3143,14 +3197,12 @@ local function world_new()
world.children = world_children world.children = world_children
world.range = world_range world.range = world_range
for i = 1, HI_COMPONENT_ID do for i = 1, EcsRest do
local e = entity_index_new_id(entity_index) entity_index_new_id(entity_index)
inner_world_add(world, e, EcsComponent)
end end
for i = HI_COMPONENT_ID + 1, EcsRest do for i = 1, max_component_id do
-- Initialize built-in components inner_world_add(world, i, EcsComponent)
entity_index_new_id(entity_index)
end end
inner_world_add(world, EcsName, EcsComponent) inner_world_add(world, EcsName, EcsComponent)

View file

@ -335,11 +335,11 @@ TEST("bulk", function()
do CASE "Should bulk add with hooks moving archetypes without previous" do CASE "Should bulk add with hooks moving archetypes without previous"
local world = jecs.world() local world = jecs.world()
local c1, c2, c3 = world:component(), world:component(), world:component() local c1, c2, c3 = world:component(), world:component(), world:component()
world:added(c1, function(e) world:added(c1, function(e)
world:set(e, c3, "hello") world:set(e, c3, "hello")
end) end)
local e = world:entity() local e = world:entity()
jecs.bulk_insert(world, e, {c1,c2}, {true, 123}) jecs.bulk_insert(world, e, {c1,c2}, {true, 123})
CHECK(world:get(e, c1) == true) CHECK(world:get(e, c1) == true)
@ -350,11 +350,11 @@ TEST("bulk", function()
do CASE "Should bulk add with hooks moving archetypes with previous" do CASE "Should bulk add with hooks moving archetypes with previous"
local world = jecs.world() local world = jecs.world()
local c1, c2, c3 = world:component(), world:component(), world:component() local c1, c2, c3 = world:component(), world:component(), world:component()
world:added(c1, function(e) world:added(c1, function(e)
world:set(e, c3, "hello") world:set(e, c3, "hello")
end) end)
local e = world:entity() local e = world:entity()
world:add(e, world:entity()) world:add(e, world:entity())
jecs.bulk_insert(world, e, {c1,c2}, {true, 123}) jecs.bulk_insert(world, e, {c1,c2}, {true, 123})
@ -550,9 +550,9 @@ TEST("world:add()", function()
end) end)
TEST("world:children()", function() TEST("world:children()", function()
local world = jecs.world()
local C = jecs.component() local C = jecs.component()
local T = jecs.tag() local T = jecs.tag()
local world = jecs.world()
local e1 = world:entity() local e1 = world:entity()
world:set(e1, C, true) world:set(e1, C, true)
@ -2510,7 +2510,7 @@ TEST("#repro2", function()
local entity = world:entity() local entity = world:entity()
world:set(entity, pair(Lifetime, Particle), 1) world:set(entity, pair(Lifetime, Particle), 1)
world:set(entity, pair(Lifetime, Beam), 2) world:set(entity, pair(Lifetime, Beam), 2)
world:set(entity, pair(4 :: any, 5 :: any), 6) -- noise world:set(entity, pair(world:component(), world:component()), 6) -- noise
CHECK(world:get(entity, pair(Lifetime, Particle)) == 1) CHECK(world:get(entity, pair(Lifetime, Particle)) == 1)
CHECK(world:get(entity, pair(Lifetime, Beam)) == 2) CHECK(world:get(entity, pair(Lifetime, Beam)) == 2)