From 635d0a86ac05a091ffb4c7641e28db248e4ee92f Mon Sep 17 00:00:00 2001 From: forgejo-actions <+forgejo-actions@users.noreply.github.com> Date: Sun, 13 Apr 2025 00:11:18 +0000 Subject: [PATCH] Sync to upstream Jecs 0.5.5-nightly.20250413T001101Z --- jecs/.luaurc | 2 +- jecs/CHANGELOG.md | 2 + jecs/addons/observers.luau | 103 ++++++++++++------------ jecs/build.txt | 2 +- jecs/jecs.luau | 27 ++++--- jecs/pesde-rbx.toml | 2 +- jecs/pesde.toml | 2 +- jecs/test.txt | 2 +- jecs/test_fulllog.txt | 156 +++++++++++-------------------------- jecs/wally.toml | 2 +- 10 files changed, 121 insertions(+), 179 deletions(-) diff --git a/jecs/.luaurc b/jecs/.luaurc index 5518c4c..f856eba 100644 --- a/jecs/.luaurc +++ b/jecs/.luaurc @@ -4,7 +4,7 @@ "testkit": "tools/testkit", "mirror": "mirror", "tools": "tools", - "addons": "addons", + "addons": "addons" }, "languageMode": "strict" } diff --git a/jecs/CHANGELOG.md b/jecs/CHANGELOG.md index 19a8ef1..8fdd827 100644 --- a/jecs/CHANGELOG.md +++ b/jecs/CHANGELOG.md @@ -23,6 +23,8 @@ The format is based on [Keep a Changelog][kac], and this project adheres to - This should allow a more lenient window for modifying data - Changed `OnRemove` to lazily lookup which archetype the entity will move to - Can now have interior structural changes within `OnRemove` hooks + - Optimized `world:has` for both single component and multiple component presence. + - This comes at the cost that it cannot check the component presence for more than 4 components at a time. If this is important, consider calling to this function multiple times. ## [0.5.0] - 2024-12-26 diff --git a/jecs/addons/observers.luau b/jecs/addons/observers.luau index a11f67c..cffce81 100644 --- a/jecs/addons/observers.luau +++ b/jecs/addons/observers.luau @@ -1,20 +1,32 @@ local jecs = require("@jecs") -local testkit = require("@testkit") + +type Observer = { + callback: (jecs.Entity) -> (), + query: jecs.Query, +} + +export type PatchedWorld = jecs.World & { + added: (PatchedWorld, jecs.Id, (e: jecs.Entity, id: jecs.Id, value: any) -> ()) -> (), + removed: (PatchedWorld, jecs.Id, (e: jecs.Entity, id: jecs.Id) -> ()) -> (), + changed: (PatchedWorld, jecs.Id, (e: jecs.Entity, id: jecs.Id) -> ()) -> (), + observer: (PatchedWorld, Observer) -> (), + monitor: (PatchedWorld, Observer) -> (), +} local function observers_new(world, description) local query = description.query local callback = description.callback - local terms = query.filter_with + local terms = query.filter_with :: { jecs.Id } if not terms then local ids = query.ids query.filter_with = ids terms = ids end - local entity_index = world.entity_index - local function emplaced(entity) + local entity_index = world.entity_index :: any + local function emplaced(entity: jecs.Entity) local r = jecs.entity_index_try_get_fast( - entity_index, entity) + entity_index, entity :: any) if not r then return @@ -33,18 +45,20 @@ local function observers_new(world, description) end end -local function world_track(world, ...) - local entity_index = world.entity_index - local terms = { ... } - local q_shim = { filter_with = terms } +local function monitors_new(world, description) + local query = description.query + local callback = description.callback + local terms = query.filter_with :: { jecs.Id } + if not terms then + local ids = query.ids + query.filter_with = ids + terms = ids + end - local n = 0 - local dense_array = {} - local sparse_array = {} - - local function emplaced(entity) + local entity_index = world.entity_index :: any + local function emplaced(entity: jecs.Entity) local r = jecs.entity_index_try_get_fast( - entity_index, entity) + entity_index, entity :: any) if not r then return @@ -52,55 +66,39 @@ local function world_track(world, ...) local archetype = r.archetype - if jecs.query_match(q_shim :: any, archetype) then - n += 1 - dense_array[n] = entity - sparse_array[entity] = n + if jecs.query_match(query, archetype) then + callback(entity, jecs.OnAdd) end end - local function removed(entity) - local i = sparse_array[entity] - if i ~= n then - dense_array[i] = dense_array[n] + local function removed(entity: jecs.Entity, component: jecs.Id) + local r = jecs.entity_index_try_get_fast( + entity_index, entity :: any) + + if not r then + return end - dense_array[n] = nil + local archetype = r.archetype + + if jecs.query_match(query, archetype) then + callback(entity, jecs.OnRemove) + end end for _, term in terms do world:added(term, emplaced) - world:changed(term, emplaced) + world:removed(term, removed) end - - local function iter() - local i = n - return function() - local row = i - if row == 0 then - return nil - end - i -= 1 - return dense_array[row] :: any - end - end - - local it = { - __iter = iter, - without = function(self, ...) - q_shim.filter_without = { ... } - return self - end - } - return setmetatable(it, it) end -local function observers_add(world) +local function observers_add(world: jecs.World & { [string]: any }): PatchedWorld local signals = { added = {}, emplaced = {}, removed = {} } + world.added = function(_, component, fn) local listeners = signals.added[component] if not listeners then @@ -109,7 +107,7 @@ local function observers_add(world) local idr = jecs.id_record_ensure(world, component) idr.hooks.on_add = function(entity) for _, listener in listeners do - listener(entity) + listener(entity, component) end end end @@ -124,7 +122,7 @@ local function observers_add(world) local idr = jecs.id_record_ensure(world, component) idr.hooks.on_change = function(entity, value) for _, listener in listeners do - listener(entity, value) + listener(entity, component, value) end end end @@ -139,7 +137,7 @@ local function observers_add(world) local idr = jecs.id_record_ensure(world, component) idr.hooks.on_remove = function(entity) for _, listener in listeners do - listener(entity) + listener(entity, component) end end end @@ -148,9 +146,10 @@ local function observers_add(world) world.signals = signals - world.track = world_track - world.observer = observers_new + + world.monitor = monitors_new + return world end diff --git a/jecs/build.txt b/jecs/build.txt index f37d24a..15b7408 100644 --- a/jecs/build.txt +++ b/jecs/build.txt @@ -1,2 +1,2 @@ modified = ["addons/observers.luau", ".luaurc", "CHANGELOG.md", "jecs.luau"] -version = "0.5.5-nightly.20250412T181729Z" +version = "0.5.5-nightly.20250413T001101Z" diff --git a/jecs/jecs.luau b/jecs/jecs.luau index cc342ce..d4da4ed 100644 --- a/jecs/jecs.luau +++ b/jecs/jecs.luau @@ -466,7 +466,9 @@ local function world_has_one_inline(world: ecs_world_t, entity: i53, id: i53): b return records[id] ~= nil end -local function world_has(world: ecs_world_t, entity: i53, ...: i53): boolean +local function world_has(world: ecs_world_t, entity: i53, + a: i53, b: i53?, c: i53?, d: i53?, e: i53?): boolean + local record = entity_index_try_get_fast(world.entity_index, entity) if not record then return false @@ -479,13 +481,11 @@ local function world_has(world: ecs_world_t, entity: i53, ...: i53): boolean local records = archetype.records - for i = 1, select("#", ...) do - if not records[select(i, ...)] then - return false - end - end - - return true + return records[a] ~= nil and + (b == nil or records[b] ~= nil) and + (c == nil or records[c] ~= nil) and + (d == nil or records[d] ~= nil) and + (e == nil or error("args exceeded")) end local function world_target(world: ecs_world_t, entity: i53, relation: i24, index: number?): i24? @@ -2420,7 +2420,10 @@ export type World = { & (self: World, id: Entity, Id, Id, Id, Id) -> (A?, B?, C?, D?), --- Returns whether the entity has the ID. - has: (self: World, entity: Entity, ...Id) -> boolean, + has: ((World, Entity, A) -> boolean) + & ((World, Entity, A, B) -> boolean) + & ((World, Entity, A, B, C) -> boolean) + & (World, Entity, A, B, C, D) -> boolean, --- Get parent (target of ChildOf relationship) for entity. If there is no ChildOf relationship pair, it will return nil. parent:(self: World, entity: Entity) -> Entity, @@ -2487,9 +2490,9 @@ return { ECS_ID_DELETE = ECS_ID_DELETE, - IS_PAIR = ECS_IS_PAIR, - pair_first = ecs_pair_first, - pair_second = ecs_pair_second, + IS_PAIR = (ECS_IS_PAIR :: any) :: (pair: Pair) -> boolean, + pair_first = (ecs_pair_first :: any) :: (world: World, pair: Pair) -> Id

, + pair_second = (ecs_pair_second :: any) :: (world: World, pair: Pair) -> Id, entity_index_get_alive = entity_index_get_alive, archetype_append_to_records = archetype_append_to_records, diff --git a/jecs/pesde-rbx.toml b/jecs/pesde-rbx.toml index f1afa27..f93026b 100644 --- a/jecs/pesde-rbx.toml +++ b/jecs/pesde-rbx.toml @@ -3,7 +3,7 @@ includes = ["init.luau", "pesde.toml", "README.md", "CHANGELOG.md", "LICENSE", " license = "MIT" name = "marked/jecs_nightly" repository = "https://git.devmarked.win/marked/jecs-nightly" -version = "0.5.5-nightly.20250412T181729Z" +version = "0.5.5-nightly.20250413T001101Z" [indices] default = "https://github.com/pesde-pkg/index" diff --git a/jecs/pesde.toml b/jecs/pesde.toml index 2d4fd9a..6b1846b 100644 --- a/jecs/pesde.toml +++ b/jecs/pesde.toml @@ -3,7 +3,7 @@ includes = ["init.luau", "pesde.toml", "README.md", "CHANGELOG.md", "LICENSE", " license = "MIT" name = "marked/jecs_nightly" repository = "https://git.devmarked.win/marked/jecs-nightly" -version = "0.5.5-nightly.20250412T181729Z" +version = "0.5.5-nightly.20250413T001101Z" [indices] default = "https://github.com/pesde-pkg/index" diff --git a/jecs/test.txt b/jecs/test.txt index 6b18d81..60eabdb 100644 --- a/jecs/test.txt +++ b/jecs/test.txt @@ -1,2 +1,2 @@ passed = true -timestamp = "20250412T181730Z" +timestamp = "20250413T001103Z" diff --git a/jecs/test_fulllog.txt b/jecs/test_fulllog.txt index f588cc4..9a47c03 100644 --- a/jecs/test_fulllog.txt +++ b/jecs/test_fulllog.txt @@ -1,77 +1,39 @@ -*created e271v0 -*created e272v0 -*created e273v0 -*created e274v0 -*created e275v0 -*created e276v0 -*created e277v0 -*created e278v0 -*created e279v0 -*created e280v0 -|-alive--| -| e271v0 | -|--------| -| e272v0 | -|--------| -| e273v0 | -|--------| -| e274v0 | -|--------| -| e275v0 | -|--------| -| e276v0 | -|--------| -| e277v0 | -|--------| -| e278v0 | -|--------| -| e279v0 | -|--------| -| e280v0 | -|--------| - - -*deleted e270v0 -*deleted e271v0 -*deleted e272v0 -*deleted e273v0 -*deleted e274v0 -*deleted e275v0 -*deleted e274v1 -*deleted e273v1 -*deleted e272v1 -*deleted e271v1 -*deleted e270v1 ----idempotent 1_2 -1_2 -7.1 us  1 kB│ delete children of entity -9.2 us  1 kB│ remove friends of entity -346 ns  0  B│ simple deletion of entity -the great reset +7.4 us  2 kB│ delete children of entity +9.5 us  1 kB│ remove friends of entity +352 ns  0  B│ simple deletion of entity +world:add() +PASS│ idempotent +PASS│ archetype move + +world:children() PASS│  -#repro3 -PASS│ should add the correct ModelBase for parts -PASS│ should add the correct ModelBase for parts -PASS│  +world:clear() +PASS│ should remove its components +PASS│ remove cleared ID from entities -#adding a recycled target -PASS│  +world:component() +PASS│ only components should have EcsComponent trait +PASS│ tag -#repro2 +world:contains() PASS│  +PASS│ should not exist after delete -another -PASS│  +world:delete() +PASS│ invoke OnRemove hooks +PASS│ delete recycled entity id used as component +PASS│ bug: Empty entity does not respect cleanup policy +PASS│ should allow deleting components +PASS│ delete entities using another Entity as component with Delete cleanup action +PASS│ delete children +PASS│ remove deleted ID from entities +PASS│ fast delete +PASS│ cycle -#repro -PASS│  - -archetype -PASS│  - -world:cleanup() +world:each() PASS│  world:entity() @@ -81,16 +43,9 @@ PASS│ Recycling PASS│ Recycling max generation -world:set() -PASS│ archetype move -PASS│ pairs - -world:remove() -PASS│ should allow remove a component that doesn't exist on entity - -world:add() -PASS│ idempotent -PASS│ archetype move +world:has() +PASS│ should find Tag on entity +PASS│ should return false when missing one tag world:query() PASS│ cached @@ -110,49 +65,32 @@ PASS│ should error when setting invalid pair PASS│ should find target for ChildOf PASS│ despawning while iterating -NONE│ iterator invalidation -SKIP│ adding -PASS│ spawning PASS│ should not find any entities -PASS│ without +PASS│ world:query():without() -world:each -PASS│  +world:remove() +PASS│ should allow remove a component that doesn't exist on entity -world:children -PASS│  - -world:clear() -PASS│ should remove its components -PASS│ remove cleared ID from entities - -world:has() -PASS│ should find Tag on entity -PASS│ should return false when missing one tag - -world:component() -PASS│ only components should have EcsComponent trait -PASS│ tag - -world:delete -PASS│ invoke OnRemove hooks -PASS│ delete recycled entity id used as component -PASS│ bug: Empty entity does not respect cleanup policy -PASS│ should allow deleting components -PASS│ delete entities using another Entity as component with Delete cleanup action -PASS│ delete children -PASS│ remove deleted ID from entities -PASS│ fast delete -PASS│ cycle +world:set() +PASS│ archetype move +PASS│ pairs world:target PASS│ nth index PASS│ infer index when unspecified PASS│ loop until no target -world:contains +#adding a recycled target +PASS│  + +#repro2 +PASS│  + +another +PASS│  + +#repro PASS│  -PASS│ should not exist after delete Hooks PASS│ OnAdd @@ -177,5 +115,5 @@ PASS│ #2 PASS│ #3 -77/77 test cases passed in 29.411 ms. +68/68 test cases passed in 29.621 ms. 0 fails diff --git a/jecs/wally.toml b/jecs/wally.toml index 544c077..982c9a3 100644 --- a/jecs/wally.toml +++ b/jecs/wally.toml @@ -5,4 +5,4 @@ license = "MIT" name = "mark-marks/jecs-nightly" realm = "shared" registry = "https://github.com/UpliftGames/wally-index" -version = "0.5.5-nightly.20250412T181729Z" +version = "0.5.5-nightly.20250413T001101Z"