From 97891d3c9e2384149217c5923fb4477f325f701a Mon Sep 17 00:00:00 2001 From: Tchips46 Date: Sun, 14 Jun 2026 20:23:00 +0900 Subject: [PATCH] feat: indexed zipper --- packages/ecs-lib/lib/libecs.d.ts | 1 + packages/ecs-lib/test/Zipper.spec.ts | 33 +++++++++++++++++------ packages/ecs-lib/wasm/Registry.cpp | 1 + packages/ecs-lib/wasm/Registry.hpp | 40 ++++++++++++++++++++++++++++ 4 files changed, 67 insertions(+), 8 deletions(-) diff --git a/packages/ecs-lib/lib/libecs.d.ts b/packages/ecs-lib/lib/libecs.d.ts index d4d8cf0b..24825128 100644 --- a/packages/ecs-lib/lib/libecs.d.ts +++ b/packages/ecs-lib/lib/libecs.d.ts @@ -50,6 +50,7 @@ export interface Registry extends ClassHandle { getComponents(_0: Component): SparseArray; spawnEntity(): Entity; getZipper(_0: Component[]): any[]; + getIndexedZipper(_0: Component[]): any[]; killEntity(_0: Entity): void; clearEntities(): void; removeComponent(_0: Entity, _1: Component): void; diff --git a/packages/ecs-lib/test/Zipper.spec.ts b/packages/ecs-lib/test/Zipper.spec.ts index 204229be..cd3b1e9a 100644 --- a/packages/ecs-lib/test/Zipper.spec.ts +++ b/packages/ecs-lib/test/Zipper.spec.ts @@ -97,18 +97,35 @@ describe("Zipper", () => { let zip = r.getZipper([Velocity]); expect(zip).toBeDefined(); - for (let i = 0; i < 20; i++) { - if (zip[i]) { - zip[i]["Velocity"].y *= 2; - } - } + zip.forEach(({ Velocity }) => { + Velocity.y *= 2; + }); zip = r.getZipper([Velocity]); + expect(zip).toBeDefined(); + + zip.forEach((entity) => { + expect(entity).toStrictEqual({ Velocity: new Velocity(0, 2) }); + }); + }); + }); + + describe("indexed zipper", () => { + it("should reflect index of the zipped entities components", async () => { + const m = await Module(); + const r = new m.Registry(); + for (let i = 0; i < 20; i++) { - if (zip[i]) { - expect(zip[i]).toStrictEqual({ Velocity: new Velocity(0, 2) }); - } + const e = r.spawnEntity(); + if (i % 5 === 0) r.addComponent(e, new Velocity(0, i)); } + + const zip = r.getIndexedZipper([Velocity]); + expect(zip).toBeDefined(); + + zip.forEach((entity, index) => { + expect(entity).toStrictEqual({ id: index * 5, Velocity: new Velocity(0, index * 5) }); + }); }); }); }); diff --git a/packages/ecs-lib/wasm/Registry.cpp b/packages/ecs-lib/wasm/Registry.cpp index 233feed5..a20dd52b 100644 --- a/packages/ecs-lib/wasm/Registry.cpp +++ b/packages/ecs-lib/wasm/Registry.cpp @@ -57,6 +57,7 @@ namespace nfo { .function("removeSystem", &Registry::remove_system) .function("clearSystems", &Registry::clear_systems) .function("getZipper", &Registry::get_zipper) + .function("getIndexedZipper", &Registry::get_indexed_zipper) .function("maxEntities", &Registry::max_entities); } } // namespace nfo diff --git a/packages/ecs-lib/wasm/Registry.hpp b/packages/ecs-lib/wasm/Registry.hpp index 4cde1ee0..5912df5a 100644 --- a/packages/ecs-lib/wasm/Registry.hpp +++ b/packages/ecs-lib/wasm/Registry.hpp @@ -323,6 +323,46 @@ namespace nfo { return ZipperOutput(arr); } + /** + * Get the indexed zipper output for the given components. + * + * @param comps An array of component types to zip. + * @return A ZipperOutput containing the id of the entity and it's zipped components. + * @throws std::runtime_error if the input is not an array. + */ + ZipperOutput get_indexed_zipper(const ZipperInput &comps) + { + if (!comps.isArray()) + throw std::runtime_error("getIndexedZipper: need an array of comps as parameter"); + + std::size_t max = SIZE_MAX; + std::map *> arrays; + for (int i = 0; i < comps["length"].as(); i++) { + SparseArray &components = get_components(Component(comps[i])); + arrays[get_js_class_name(comps[i])] = &components; + max = (std::min)(components.size(), max); + } + + emscripten::val arr = emscripten::val::array(); + std:size_t zipper_idx = 0; + for (std::size_t idx = 0; idx < max; idx++) { + emscripten::val obj = emscripten::val::object(); + bool need_to_add = true; + for (const auto &[name, sparse_array] : arrays) { + if (!(*sparse_array)[idx].has_value()) { + need_to_add = false; + break; + } + obj.set(name, (*sparse_array)[idx].value()); + } + if (need_to_add) { + obj.set("id", idx); + arr.set(zipper_idx++, obj); + } + } + return ZipperOutput(arr); + } + private: std::unordered_map _components_arrays;