Fix bits arrangement in IDs
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-03-24 17:36:13 +01:00
parent e073a570b7
commit 9d83c3bc13
3 changed files with 95 additions and 120 deletions

View file

@ -28,22 +28,22 @@ local B6 = ecr.component()
local B7 = ecr.component() local B7 = ecr.component()
local B8 = ecr.component() local B8 = ecr.component()
local C1 = ecs:entity() local C1 = ecs:component()
local C2 = ecs:entity() local C2 = ecs:component()
local C3 = ecs:entity() local C3 = ecs:component()
local C4 = ecs:entity() local C4 = ecs:component()
local C5 = ecs:entity() local C5 = ecs:component()
local C6 = ecs:entity() local C6 = ecs:component()
local C7 = ecs:entity() local C7 = ecs:component()
local C8 = ecs:entity() local C8 = ecs:component()
local E1 = mcs:entity() local E1 = mcs:component()
local E2 = mcs:entity() local E2 = mcs:component()
local E3 = mcs:entity() local E3 = mcs:component()
local E4 = mcs:entity() local E4 = mcs:component()
local E5 = mcs:entity() local E5 = mcs:component()
local E6 = mcs:entity() local E6 = mcs:component()
local E7 = mcs:entity() local E7 = mcs:component()
local E8 = mcs:entity() local E8 = mcs:component()
local registry2 = ecr.registry() local registry2 = ecr.registry()
return { return {
@ -52,48 +52,30 @@ return {
end, end,
Functions = { Functions = {
Matter = function() Mirror = function()
local e = newWorld:spawn() local e = mcs:entity()
for i = 1, 5000 do for i = 1, 5000 do
newWorld:insert( mcs:set(e, E1, false)
e, mcs:set(e, E2, false)
A1({ value = true }), mcs:set(e, E3, false)
A2({ value = true }), mcs:set(e, E4, false)
A3({ value = true }), mcs:set(e, E5, false)
A4({ value = true }), mcs:set(e, E6, false)
A5({ value = true }), mcs:set(e, E7, false)
A6({ value = true }), mcs:set(e, E8, false)
A7({ value = true }),
A8({ value = true })
)
end end
end, end,
ECR = function()
local e = registry2.create()
for i = 1, 5000 do
registry2:set(e, B1, { value = false })
registry2:set(e, B2, { value = false })
registry2:set(e, B3, { value = false })
registry2:set(e, B4, { value = false })
registry2:set(e, B5, { value = false })
registry2:set(e, B6, { value = false })
registry2:set(e, B7, { value = false })
registry2:set(e, B8, { value = false })
end
end,
Jecs = function() Jecs = function()
local e = ecs:entity() local e = ecs:entity()
for i = 1, 5000 do for i = 1, 5000 do
ecs:set(e, C1, { value = false }) ecs:set(e, C1, false)
ecs:set(e, C2, { value = false }) ecs:set(e, C2, false)
ecs:set(e, C3, { value = false }) ecs:set(e, C3, false)
ecs:set(e, C4, { value = false }) ecs:set(e, C4, false)
ecs:set(e, C5, { value = false }) ecs:set(e, C5, false)
ecs:set(e, C6, { value = false }) ecs:set(e, C6, false)
ecs:set(e, C7, { value = false }) ecs:set(e, C7, false)
ecs:set(e, C8, { value = false }) ecs:set(e, C8, false)
end end
end, end,
}, },

View file

@ -90,60 +90,35 @@ local EcsOnArchetypeCreate = HI_COMPONENT_ID + 12
local EcsOnArchetypeDelete = HI_COMPONENT_ID + 13 local EcsOnArchetypeDelete = HI_COMPONENT_ID + 13
local EcsRest = HI_COMPONENT_ID + 14 local EcsRest = HI_COMPONENT_ID + 14
local ECS_PAIR_FLAG = 0x8
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_DELETE = 0b0000_0001 local ECS_ID_DELETE = 0b0000_0001
local ECS_ID_IS_TAG = 0b0000_0010 local ECS_ID_IS_TAG = 0b0000_0010
local ECS_ID_HAS_ON_ADD = 0b0000_0100 local ECS_ID_HAS_ON_ADD = 0b0000_0100
local ECS_ID_HAS_ON_SET = 0b0000_1000 local ECS_ID_HAS_ON_SET = 0b0000_1000
local ECS_ID_HAS_ON_REMOVE = 0b0001_0000 local ECS_ID_HAS_ON_REMOVE = 0b0001_0000
local ECS_ID_MASK = 0b0000_0000 local ECS_ID_MASK = 0b0000_0000
-- stylua: ignore end
local NULL_ARRAY = table.freeze({}) :: Column
local function FLAGS_ADD(is_pair: boolean): number local ECS_ENTITY_MASK = bit32.lshift(1, 24)
local flags = 0x0 local ECS_GENERATION_MASK = bit32.lshift(1, 16)
if is_pair then local NULL_ARRAY = table.freeze({})
flags = bit32.bor(flags, ECS_PAIR_FLAG) -- HIGHEST bit in the ID.
end
if false then
flags = bit32.bor(flags, 0x4) -- Set the second flag to true
end
if false then
flags = bit32.bor(flags, 0x2) -- Set the third flag to true
end
if false then
flags = bit32.bor(flags, 0x1) -- LAST BIT in the ID.
end
return flags
end local function ECS_COMBINE(id: number, generation: number): i53
return id + (generation * ECS_ENTITY_MASK)
local function ECS_COMBINE(source: number, target: number): i53
return (source * 268435456) + (target * ECS_ID_FLAGS_MASK)
end end
local ECS_PAIR_OFFSET = 2^48
local function ECS_IS_PAIR(e: number): boolean local function ECS_IS_PAIR(e: number): boolean
return if e > ECS_ENTITY_MASK then (e % ECS_ID_FLAGS_MASK) // ECS_PAIR_FLAG ~= 0 else false return e > ECS_PAIR_OFFSET
end
-- HIGH 24 bits LOW 24 bits
local function ECS_GENERATION(e: i53): i24
return if e > ECS_ENTITY_MASK then (e // ECS_ID_FLAGS_MASK) % ECS_GENERATION_MASK else 0
end end
local function ECS_GENERATION_INC(e: i53) local function ECS_GENERATION_INC(e: i53)
if e > ECS_ENTITY_MASK then if e > ECS_ENTITY_MASK then
local flags = e // ECS_ID_FLAGS_MASK local id = e % ECS_ENTITY_MASK
local id = flags // ECS_ENTITY_MASK local generation = e // ECS_ENTITY_MASK
local generation = flags % ECS_GENERATION_MASK
local next_gen = generation + 1 local next_gen = generation + 1
if next_gen > ECS_GENERATION_MASK then if next_gen >= ECS_GENERATION_MASK then
return id return id
end end
@ -152,22 +127,23 @@ local function ECS_GENERATION_INC(e: i53)
return ECS_COMBINE(e, 1) return ECS_COMBINE(e, 1)
end end
-- FIRST gets the high ID
local function ECS_ENTITY_T_HI(e: i53): i24
return if e > ECS_ENTITY_MASK then (e // ECS_ID_FLAGS_MASK) % ECS_ENTITY_MASK else e
end
-- SECOND
local function ECS_ENTITY_T_LO(e: i53): i24 local function ECS_ENTITY_T_LO(e: i53): i24
return if e > ECS_ENTITY_MASK then (e // ECS_ID_FLAGS_MASK) // ECS_ENTITY_MASK else e return e % ECS_ENTITY_MASK
end end
local function _STRIP_GENERATION(e: i53): i24 local function ECS_GENERATION(e: i53)
return ECS_ENTITY_T_LO(e) return e // ECS_ENTITY_MASK
end
local function ECS_ENTITY_T_HI(e: i53): i24
return e // ECS_ENTITY_MASK
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(pred), ECS_ENTITY_T_LO(obj)) + FLAGS_ADD(--[[isPair]] true) :: i53 pred %= ECS_ENTITY_MASK
obj %= ECS_ENTITY_MASK
return obj + (pred * 2^24) + ECS_PAIR_OFFSET
end end
local function entity_index_try_get_any(entity_index: EntityIndex, entity: number): Record? local function entity_index_try_get_any(entity_index: EntityIndex, entity: number): Record?
@ -236,14 +212,14 @@ local function entity_index_new_id(entity_index: EntityIndex): i53
return id return id
end end
-- ECS_PAIR_FIRST, gets the relationship target / obj / HIGH bits
local function ecs_pair_first(world, e) local function ecs_pair_first(world, e)
return entity_index_get_alive(world.entity_index, ECS_ENTITY_T_LO(e)) local pred = (e - ECS_PAIR_OFFSET) // ECS_ENTITY_MASK
return entity_index_get_alive(world.entity_index, pred)
end end
-- ECS_PAIR_SECOND gets the relationship / pred / LOW bits
local function ecs_pair_second(world, e) local function ecs_pair_second(world, e)
return entity_index_get_alive(world.entity_index, ECS_ENTITY_T_HI(e)) local obj = (e - ECS_PAIR_OFFSET) % ECS_ENTITY_MASK
return entity_index_get_alive(world.entity_index, obj)
end end
local function query_match(query, archetype: Archetype) local function query_match(query, archetype: Archetype)
@ -476,8 +452,8 @@ local function world_target(world: World, entity: i53, relation: i24, index: num
return nil return nil
end end
if nth >= count then if nth > count then
nth = nth + count + 1 nth = nth + count
end end
local tr = idr.cache[archetype_id] local tr = idr.cache[archetype_id]

View file

@ -241,19 +241,6 @@ local pe = require("@tools/entity_visualiser").prettify
local lifetime_tracker_add = require("@tools/lifetime_tracker") local lifetime_tracker_add = require("@tools/lifetime_tracker")
TEST("world:entity()", function() TEST("world:entity()", function()
do CASE "ensure id"
local w = world_new()
w = lifetime_tracker_add(w, {padding_enabled=false})
local id = w:entity()
CHECK(id == w:entity(id))
CHECK(w:entity(999 :: any) == 999 :: any)
w:print_snapshot()
local id1 = w:entity(999)
CHECK(w:contains(999))
CHECK(not w:contains(998))
print(w:entity(), "newest id after 999")
end
do do
CASE("unique IDs") CASE("unique IDs")
local world = jecs.World.new() local world = jecs.World.new()
@ -1388,10 +1375,15 @@ TEST("world:target", function()
world:add(e, pair(C, D)) world:add(e, pair(C, D))
CHECK(pair(A, B) < pair(A, C)) CHECK(pair(A, B) < pair(A, C))
CHECK(pair(A, E) < pair(B, C)) CHECK(pair(A, C) < pair(A, D))
CHECK(pair(C, A) < pair(C, D))
local records = debug_world_inspect(world).records(e) local records = debug_world_inspect(world).records(e)
CHECK(jecs.pair_first(world, pair(B, C)) == B) CHECK(jecs.pair_first(world, pair(B, C)) == B)
local r = jecs.entity_index_try_get(world.entity_index, e)
local archetype = r.archetype
local counts = archetype.counts
CHECK(counts[pair(A, __)] == 4)
CHECK(records[pair(B, C)] > records[pair(A, E)]) CHECK(records[pair(B, C)] > records[pair(A, E)])
CHECK(world:target(e, A, 0) == B) CHECK(world:target(e, A, 0) == B)
CHECK(world:target(e, A, 1) == C) CHECK(world:target(e, A, 1) == C)
@ -1401,6 +1393,28 @@ TEST("world:target", function()
CHECK(world:target(e, B, 1) == D) CHECK(world:target(e, B, 1) == D)
CHECK(world:target(e, C, 0) == D) CHECK(world:target(e, C, 0) == D)
CHECK(world:target(e, C, 1) == nil) CHECK(world:target(e, C, 1) == nil)
-- for id in archetype.records do
-- local f = world:get(ecs_pair_first(world, id), jecs.Name)
-- local s = world:get(ecs_pair_second(world, id), jecs.Name)
-- print(`({f}, {s})`)
-- end
--
CHECK(archetype.records[pair(A, B)] == 1)
CHECK(archetype.records[pair(A, C)] == 2)
CHECK(archetype.records[pair(A, D)] == 3)
CHECK(archetype.records[pair(A, E)] == 4)
-- print("(A, B)", archetype.records[pair(A, B)])
-- print("(A, C)", archetype.records[pair(A, C)])
-- print("(A, D)", archetype.records[pair(A, D)])
-- print("(A, E)", archetype.records[pair(A, E)])
-- print(pair(A, D), pair(B, C))
-- print("(B, C)", archetype.records[pair(B, C)])
CHECK(world:target(e, C, 0) == D)
CHECK(world:target(e, C, 1) == nil)
end end
do do
@ -1516,7 +1530,7 @@ TEST("Hooks", function()
world:set(e, A, true) world:set(e, A, true)
world:remove(e, A) world:remove(e, A)
CHECK(not world:get(e, A)) CHECK(not world:get(e, A))
CHECK(not world:get(e, B)) CHECK(world:get(e, B))
end end
end end
end) end)
@ -1596,7 +1610,6 @@ TEST("repro", function()
if cooldown <= 0 then if cooldown <= 0 then
table.insert(toRemove, id) table.insert(toRemove, id)
print("removing")
-- world:remove(id, components.Cooldown) -- world:remove(id, components.Cooldown)
else else
world:set(id, components.Cooldown, cooldown) world:set(id, components.Cooldown, cooldown)
@ -1645,6 +1658,10 @@ TEST("wildcard query", function()
local entity = world:entity() local entity = world:entity()
local p = pair(Relation, A)
CHECK(jecs.pair_first(world, p) == Relation)
CHECK(jecs.pair_second(world, p) == A)
local w = dwi(world)
world:add(entity, pair(Relation, A)) world:add(entity, pair(Relation, A))
local counter = 0 local counter = 0