Add dual types

This commit is contained in:
Ukendio 2025-03-25 23:13:53 +01:00
parent 9d83c3bc13
commit f3befa3adb
4 changed files with 502 additions and 373 deletions

715
jecs.luau

File diff suppressed because it is too large Load diff

View file

@ -5,21 +5,21 @@ local lifetime_tracker_add = require("@tools/lifetime_tracker")
local pe = require("@tools/entity_visualiser").prettify local pe = require("@tools/entity_visualiser").prettify
local world = lifetime_tracker_add(jecs.world(), {padding_enabled=false}) local world = lifetime_tracker_add(jecs.world(), {padding_enabled=false})
local FriendsWith = world:component() local FriendsWith = world:component()
local _1 = world:print_snapshot() world:print_snapshot()
local e1 = world:entity() local e1 = world:entity()
local e2 = world:entity() local e2 = world:entity()
world:delete(e2) world:delete(e2)
local _2 = world:print_snapshot() world:print_snapshot()
local e3 = world:entity() local e3 = world:entity()
world:add(e3, pair(ChildOf, e1)) world:add(e3, pair(ChildOf, e1))
local e4 = world:entity() local e4 = world:entity()
world:add(e4, pair(FriendsWith, e3)) world:add(e4, pair(FriendsWith, e3))
local _3 = world:print_snapshot() world:print_snapshot()
world:delete(e1) world:delete(e1)
world:delete(e3) world:delete(e3)
local _4 = world:print_snapshot() world:print_snapshot()
world:print_entity_index() world:print_entity_index()
world:entity() world:entity()
world:entity() world:entity()
local _5 = world:print_snapshot() world:print_snapshot()

View file

@ -117,6 +117,34 @@ local function name(world, e)
return world:get(e, jecs.Name) return world:get(e, jecs.Name)
end end
TEST("#repro2", function()
local world = world_new()
local Lifetime = world:component() :: jecs.Id<number>
local Particle = world:entity()
local Beam = world:entity()
local entity = world:entity()
world:set(entity, pair(Lifetime, Particle), 1)
world:set(entity, pair(Lifetime, Beam), 2)
for e in world:each(pair(Lifetime, __)) do
local i = 0
local nth = world:target(e, Lifetime, i)
while nth do
local data = world:get(e, pair(Lifetime, nth))
if nth == Particle then
CHECK(data == 1)
elseif nth == Beam then
CHECK(data == 2)
else
CHECK(false)
end
i += 1
nth = world:target(e, Lifetime, i)
end
end
end)
TEST("#repro", function() TEST("#repro", function()
local world = world_new() local world = world_new()
@ -1394,24 +1422,10 @@ TEST("world:target", function()
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, B)] == 1)
CHECK(archetype.records[pair(A, C)] == 2) CHECK(archetype.records[pair(A, C)] == 2)
CHECK(archetype.records[pair(A, D)] == 3) CHECK(archetype.records[pair(A, D)] == 3)
CHECK(archetype.records[pair(A, E)] == 4) 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, 0) == D)
CHECK(world:target(e, C, 1) == nil) CHECK(world:target(e, C, 1) == nil)

108
tools/runtime_lints.luau Normal file
View file

@ -0,0 +1,108 @@
local function dbg_info(n: number): any
return debug.info(n, "s")
end
local function throw(msg: string)
local s = 1
local root = dbg_info(1)
repeat
s += 1
until dbg_info(s) ~= root
if warn then
error(msg, s)
else
print(`[jecs] error: {msg}\n`)
end
end
local function ASSERT<T>(v: T, msg: string)
if v then
return
end
throw(msg)
end
local function runtime_lints_add(world)
local function get_name(id)
return world_get_one_inline(world, id, EcsName)
end
local function bname(id): string
local name: string
if ECS_IS_PAIR(id) then
local first = get_name(world, ecs_pair_first(world, id))
local second = get_name(world, ecs_pair_second(world, id))
name = `pair({first}, {second})`
else
return get_name(world, id)
end
if name then
return name
else
return `${id}`
end
end
local function ID_IS_TAG(world: World, id)
if ECS_IS_PAIR(id) then
id = ecs_pair_first(world, id)
end
return not world_has_one_inline(world, id, EcsComponent)
end
World.query = function(world: World, ...)
ASSERT((...), "Requires at least a single component")
return world_query(world, ...)
end
World.set = function(world: World, entity: i53, id: i53, value: any): ()
local is_tag = ID_IS_TAG(world, id)
if is_tag and value == nil then
local _1 = bname(world, entity)
local _2 = bname(world, id)
local why = "cannot set component value to nil"
throw(why)
return
elseif value ~= nil and is_tag then
local _1 = bname(world, entity)
local _2 = bname(world, id)
local why = `cannot set a component value because {_2} is a tag`
why ..= `\n[jecs] note: consider using "world:add({_1}, {_2})" instead`
throw(why)
return
end
world_set(world, entity, id, value)
end
World.add = function(world: World, entity: i53, id: i53, value: any)
if value ~= nil then
local _1 = bname(world, entity)
local _2 = bname(world, id)
throw("You provided a value when none was expected. " .. `Did you mean to use "world:add({_1}, {_2})"`)
end
world_add(world, entity, id)
end
World.get = function(world: World, entity: i53, ...)
local length = select("#", ...)
ASSERT(length < 5, "world:get does not support more than 4 components")
local _1
for i = 1, length do
local id = select(i, ...)
local id_is_tag = not world_has(world, id, EcsComponent)
if id_is_tag then
local name = get_name(world, id)
if not _1 then
_1 = get_name(world, entity)
end
throw(
`cannot get (#{i}) component {name} value because it is a tag.`
.. `\n[jecs] note: If this was intentional, use "world:has({_1}, {name}) instead"`
)
end
end
return world_get(world, entity, ...)
end
end