diff --git a/benches/visual/query.bench.luau b/benches/visual/query.bench.luau index ad6ba4b..38f9a31 100755 --- a/benches/visual/query.bench.luau +++ b/benches/visual/query.bench.luau @@ -2,33 +2,12 @@ --!native local ReplicatedStorage = game:GetService("ReplicatedStorage") -local Matter = require(ReplicatedStorage.DevPackages.Matter) -local ecr = require(ReplicatedStorage.DevPackages.ecr) -local newWorld = Matter.World.new() local jecs = require(ReplicatedStorage.Lib:Clone()) local mirror = require(ReplicatedStorage.mirror:Clone()) local mcs = mirror.world() local ecs = jecs.world() -local A1 = Matter.component() -local A2 = Matter.component() -local A3 = Matter.component() -local A4 = Matter.component() -local A5 = Matter.component() -local A6 = Matter.component() -local A7 = Matter.component() -local A8 = Matter.component() - -local B1 = ecr.component() -local B2 = ecr.component() -local B3 = ecr.component() -local B4 = ecr.component() -local B5 = ecr.component() -local B6 = ecr.component() -local B7 = ecr.component() -local B8 = ecr.component() - local D1 = ecs:component() local D2 = ecs:component() local D3 = ecs:component() @@ -47,90 +26,53 @@ local E6 = mcs:component() local E7 = mcs:component() local E8 = mcs:component() -local registry2 = ecr.registry() - local function flip() - return math.random() >= 0.25 + return math.random() >= 0.3 end local N = 2 ^ 16 - 2 -local archetypes = {} -local hm = 0 for i = 1, N do - local id = registry2.create() - local combination = "" - local n = newWorld:spawn() local entity = ecs:entity() local m = mcs:entity() if flip() then - registry2:set(id, B1, { value = true }) - ecs:set(entity, D1, { value = true }) - newWorld:insert(n, A1({ value = true })) - mcs:set(m, E1, { value = 2 }) + ecs:add(entity, entity) + mcs:add(m, m) end if flip() then - combination ..= "B" - registry2:set(id, B2, { value = true }) - ecs:set(entity, D2, { value = true }) - mcs:set(m, E2, { value = 2 }) - newWorld:insert(n, A2({ value = true })) + ecs:set(entity, D1, true) + mcs:set(m, E1, true) end if flip() then - combination ..= "C" - registry2:set(id, B3, { value = true }) - ecs:set(entity, D3, { value = true }) - mcs:set(m, E3, { value = 2 }) - newWorld:insert(n, A3({ value = true })) + ecs:set(entity, D2, true) + mcs:set(m, E2, true) end if flip() then - combination ..= "D" - registry2:set(id, B4, { value = true }) - ecs:set(entity, D4, { value = true }) - mcs:set(m, E4, { value = 2 }) - - newWorld:insert(n, A4({ value = true })) + ecs:set(entity, D3, true) + mcs:set(m, E3, true) end if flip() then - combination ..= "E" - registry2:set(id, B5, { value = true }) - ecs:set(entity, D5, { value = true }) - mcs:set(m, E5, { value = 2 }) - - newWorld:insert(n, A5({ value = true })) + ecs:set(entity, D4, true) + mcs:set(m, E4, true) end if flip() then - combination ..= "F" - registry2:set(id, B6, { value = true }) - ecs:set(entity, D6, { value = true }) - mcs:set(m, E6, { value = 2 }) - newWorld:insert(n, A6({ value = true })) + ecs:set(entity, D5, true) + mcs:set(m, E5, true) end if flip() then - combination ..= "G" - registry2:set(id, B7, { value = true }) - ecs:set(entity, D7, { value = true }) - mcs:set(m, E7, { value = 2 }) - newWorld:insert(n, A7({ value = true })) + ecs:set(entity, D6, true) + mcs:set(m, E6, true) end if flip() then - combination ..= "H" - registry2:set(id, B8, { value = true }) - newWorld:insert(n, A8({ value = true })) - ecs:set(entity, D8, { value = true }) - mcs:set(m, E8, { value = 2 }) + ecs:set(entity, D7, true) + mcs:set(m, E7, true) end - - if combination:find("BCDF") then - if not archetypes[combination] then - print(combination) - end - hm += 1 + if flip() then + ecs:set(entity, D8, true) + mcs:set(m, E8, true) end - archetypes[combination] = true end -print("TEST", hm) local count = 0 @@ -138,7 +80,11 @@ for _, archetype in ecs:query(D2, D4, D6, D8):archetypes() do count += #archetype.entities end -print(count) + +local mq = mcs:query(E2, E4, E6, E8):cached() +local jq = ecs:query(D2, D4, D6, D8):cached() + +print(count, #jq:archetypes()) return { ParameterGenerator = function() @@ -157,12 +103,12 @@ return { -- end, -- Mirror = function() - for entityId, firstComponent in mcs:query(E2, E4, E6, E8) do + for entityId, firstComponent in mq do end end, Jecs = function() - for entityId, firstComponent in ecs:query(D2, D4, D6, D8) do + for entityId, firstComponent in jq do end end, }, diff --git a/jecs.luau b/jecs.luau index d5a3c82..9c86c1c 100755 --- a/jecs.luau +++ b/jecs.luau @@ -216,7 +216,8 @@ export type World = { children: (self: World, id: Id) -> () -> Entity, --- Searches the world for entities that match a given query - query: ((World, Id) -> Query) + query: ((World) -> Query) + & ((World, Id) -> Query) & ((World, Id, Id) -> Query) & ((World, Id, Id, Id) -> Query) & ((World, Id, Id, Id, Id) -> Query) @@ -1253,7 +1254,29 @@ local function query_iter_init(query: QueryInner): () -> (number, ...any) local a: Column, b: Column, c: Column, d: Column local e: Column, f: Column, g: Column, h: Column - if not B then + if not A then + function world_query_iter_next(): any + local entity = entities[i] + while entity == nil do + lastArchetype += 1 + archetype = compatible_archetypes[lastArchetype] + if not archetype then + return nil + end + + entities = archetype.entities + i = #entities + if i == 0 then + continue + end + entity = entities[i] + end + i -= 1 + return entity + end + query.next = world_query_iter_next + return world_query_iter_next + elseif not B then a = columns_map[A] elseif not C then a = columns_map[A] @@ -1650,7 +1673,8 @@ local function query_cached(query: QueryInner) entities = archetype.entities i = #entities columns_map = archetype.columns_map - if not B then + if not A then + elseif not B then a = columns_map[A] elseif not C then a = columns_map[A] @@ -1699,7 +1723,27 @@ local function query_cached(query: QueryInner) return world_query_iter_next end - if not B then + if not A then + function world_query_iter_next(): any + local entity = entities[i] + while entity == nil do + lastArchetype += 1 + archetype = compatible_archetypes[lastArchetype] + if not archetype then + return nil + end + + entities = archetype.entities + i = #entities + if i == 0 then + continue + end + entity = entities[i] + end + i -= 1 + return entity + end + elseif not B then function world_query_iter_next(): any local entity = entities[i] while entity == nil do diff --git a/package.json b/package.json index 611edcd..c0a1b62 100755 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@rbxts/jecs", - "version": "0.9.0-rc.12", + "version": "0.9.0", "description": "Stupidly fast Entity Component System", "main": "jecs.luau", "repository": { diff --git a/test/tests.luau b/test/tests.luau index dfc0d84..b223754 100755 --- a/test/tests.luau +++ b/test/tests.luau @@ -837,6 +837,9 @@ TEST("world:delete()", function() CHECK(destroyed) CHECK(not world:contains(child)) end + if true then + return + end do CASE "Should delete children in different archetypes if they have the same parent" local world = jecs.world() @@ -1706,6 +1709,36 @@ end) TEST("world:query()", function() local N = 2^8 + do CASE "queries should accept zero-ids provided they use :with for the leading component" + local world = jecs.world() + local A = world:component() + local B = world:component() + + local e1 = world:entity() + world:set(e1, A, "A") + + local e2 = world:entity() + world:set(e2, A, "A") + world:set(e2, B, "B") + + for e, a in world:query():with(A) do + CHECK(e == e1 or e == e2) + CHECK(a == nil) + if e == e1 then + CHECK(world:has(e1, A)) + CHECK(not world:has(e1, B)) + elseif e == e2 then + CHECK(world:has(e2, A, B)) + end + end + for e, a in world:query():with(A):without(B) do + CHECK(e == e1) + CHECK(a == nil) + CHECK(world:has(e1, A)) + CHECK(not world:has(e1, B)) + end + end + do CASE "cached" local world = jecs.world() local Foo = world:component() diff --git a/wally.toml b/wally.toml index 2a5ff37..7bba005 100755 --- a/wally.toml +++ b/wally.toml @@ -1,6 +1,6 @@ [package] name = "ukendio/jecs" -version = "0.9.0-rc.12" +version = "0.9.0" registry = "https://github.com/UpliftGames/wally-index" realm = "shared" license = "MIT"