diff --git a/.gitignore b/.gitignore index 2b3da0b8..5ccf52cb 100644 --- a/.gitignore +++ b/.gitignore @@ -227,3 +227,5 @@ dist .nx/ .cursor/rules/nx-rules.mdc .github/instructions/nx.instructions.md + +compile_commands.json diff --git a/example/pong/src/collisions.ts b/example/pong/src/collisions.ts index 3ba113cb..19525e63 100644 --- a/example/pong/src/collisions.ts +++ b/example/pong/src/collisions.ts @@ -1,8 +1,8 @@ -import { type ECSRegistry } from "@nanoforge/ecs"; +import { type Registry } from "@nanoforge/ecs"; import { Hitbox, Position } from "./components"; -export function checkCollisions(registry: ECSRegistry, entity: any) { +export function checkCollisions(registry: Registry, entity: any) { const entities = registry.getZipper([Hitbox, Position]); const { x, y } = entity.Position; diff --git a/example/pong/src/systems.ts b/example/pong/src/systems.ts index 02c433f0..56e191ad 100644 --- a/example/pong/src/systems.ts +++ b/example/pong/src/systems.ts @@ -1,5 +1,5 @@ import { type Context } from "@nanoforge/common"; -import { type ECSRegistry } from "@nanoforge/ecs"; +import { type Registry } from "@nanoforge/ecs"; import { type InputLibrary } from "@nanoforge/input"; import { type SoundLibrary } from "@nanoforge/sound"; @@ -14,7 +14,7 @@ import { Velocity, } from "./components"; -export function move(registry: ECSRegistry) { +export function move(registry: Registry) { const entities = registry.getZipper([Bounce, Position, Velocity]); entities.forEach((entity) => { @@ -23,7 +23,7 @@ export function move(registry: ECSRegistry) { }); } -export function bounce(registry: ECSRegistry, ctx: Context) { +export function bounce(registry: Registry, ctx: Context) { const entities = registry.getZipper([Bounce, Position, Velocity]); entities.forEach((entity) => { @@ -39,7 +39,7 @@ export function bounce(registry: ECSRegistry, ctx: Context) { }); } -export function controlPlayer(registry: ECSRegistry, ctx: Context) { +export function controlPlayer(registry: Registry, ctx: Context) { const entities = registry.getZipper([Controller, Position, Hitbox, Velocity]); entities.forEach((entity) => { @@ -62,7 +62,7 @@ export function controlPlayer(registry: ECSRegistry, ctx: Context) { }); } -export function drawCircle(registry: ECSRegistry) { +export function drawCircle(registry: Registry) { const entities = registry.getZipper([CircleComponent, Position]); entities.forEach((entity) => { @@ -71,7 +71,7 @@ export function drawCircle(registry: ECSRegistry) { }); } -export function moveRectangle(registry: ECSRegistry) { +export function moveRectangle(registry: Registry) { const entities = registry.getZipper([RectangleComponent, Position, Hitbox]); entities.forEach((entity) => { diff --git a/packages/ecs/.gitignore b/packages/ecs/.gitignore index c5431c07..a079e135 100644 --- a/packages/ecs/.gitignore +++ b/packages/ecs/.gitignore @@ -88,8 +88,9 @@ # .idea/modules # *.iml # *.ipr +!build/ -# CMake +# CMakeg cmake-build-*/ # Mongo Explorer plugin diff --git a/packages/ecs/.prettierignore b/packages/ecs/.prettierignore index e814a634..2ee93c45 100644 --- a/packages/ecs/.prettierignore +++ b/packages/ecs/.prettierignore @@ -6,3 +6,4 @@ bun.lock *.js *.d.ts +build diff --git a/packages/ecs/Makefile b/packages/ecs/Makefile index ce863f6f..739888da 100644 --- a/packages/ecs/Makefile +++ b/packages/ecs/Makefile @@ -46,7 +46,10 @@ $(HTML_NAME): $(OBJ) $(TS_NAME): LDFLAGS += -s MODULARIZE=1 -s EXPORT_ES6=1 -s ENVIRONMENT=web $(TS_NAME): $(OBJ) @mkdir -p $(OUT_DIR) - $(CC) $(OBJ) $(LDFLAGS) --emit-tsd $(TS_NAME) -o $(JS_NAME) + $(CC) $(OBJ) $(LDFLAGS) --emit-tsd libecs-tmp.d.ts -o $(JS_NAME) + cat build/pre.ts > $(OUT_DIR)/$(TS_NAME) + cat lib/libecs-tmp.d.ts >> $(OUT_DIR)/$(TS_NAME) + $(RM) lib/libecs-tmp.d.ts clean: $(RM) $(OBJ) @@ -57,7 +60,10 @@ fclean: clean tests: LDFLAGS += -s MODULARIZE=1 tests: $(OBJ) @mkdir -p $(OUT_DIR) - $(CC) $(OBJ) $(LDFLAGS) --emit-tsd $(TS_NAME) -o $(JS_NAME) + $(CC) $(OBJ) $(LDFLAGS) --emit-tsd libecs-tmp.d.ts -o $(JS_NAME) + cat build/pre.ts > $(OUT_DIR)/$(TS_NAME) + cat lib/libecs-tmp.d.ts >> $(OUT_DIR)/$(TS_NAME) + $(RM) lib/libecs-tmp.d.ts re: fclean all diff --git a/packages/ecs/build/pre.ts b/packages/ecs/build/pre.ts new file mode 100644 index 00000000..ac2a3068 --- /dev/null +++ b/packages/ecs/build/pre.ts @@ -0,0 +1 @@ +import { Context } from "@nanoforge/common" \ No newline at end of file diff --git a/packages/ecs/eslint.config.js b/packages/ecs/eslint.config.js index 7ea4a2c8..8403b06f 100644 --- a/packages/ecs/eslint.config.js +++ b/packages/ecs/eslint.config.js @@ -1,3 +1,3 @@ import config from "@nanoforge/utils-eslint-config"; -export default config; +export default [...config, { ignores: ["build"] }]; diff --git a/packages/ecs/lib/libecs.d.ts b/packages/ecs/lib/libecs.d.ts index f8c646d4..9f62205a 100644 --- a/packages/ecs/lib/libecs.d.ts +++ b/packages/ecs/lib/libecs.d.ts @@ -1,25 +1,15 @@ +import { Context } from "@nanoforge/common"; + // TypeScript bindings for emscripten-generated code. Automatically generated at compile time. -declare namespace RuntimeExports { - let HEAPF32: any; - let HEAPF64: any; - let HEAP_DATA_VIEW: any; - let HEAP8: any; - let HEAPU8: any; - let HEAP16: any; - let HEAPU16: any; - let HEAP32: any; - let HEAPU32: any; - let HEAP64: any; - let HEAPU64: any; -} -interface WasmModule { -} +interface WasmModule {} export interface ClassHandle { isAliasOf(other: ClassHandle): boolean; delete(): void; deleteLater(): this; isDeleted(): boolean; + // @ts-ignore - If targeting lower than ESNext, this symbol might not exist. + [Symbol.dispose](): void; clone(): this; } export interface container extends ClassHandle { @@ -50,40 +40,44 @@ export interface Entity extends ClassHandle { getId(): number; } +type Component = { name: string; [key: string]: any }; + +type System = (registry: Registry, ctx: Context) => void; + export interface Registry extends ClassHandle { - registerComponent(_0: {name: string, [key: string]: any}): SparseArray; - getComponentsConst(_0: {name: string, [key: string]: any}): SparseArray; - getComponents(_0: {name: string, [key: string]: any}): SparseArray; + registerComponent(_0: Component): SparseArray; + getComponentsConst(_0: Component): SparseArray; + getComponents(_0: Component): SparseArray; spawnEntity(): Entity; + getZipper(_0: Component[]): any[]; killEntity(_0: Entity): void; clearEntities(): void; - removeComponent(_0: Entity, _1: {name: string, [key: string]: any}): void; - addSystem(_0: (registry: Registry, ctx: any) => void): void; + removeComponent(_0: Entity, _1: Component): void; + addSystem(_0: System): void; clearSystems(): void; entityFromIndex(_0: number): Entity; removeSystem(_0: number): void; maxEntities(): number; - getEntityComponentConst(_0: Entity, _1: {name: string, [key: string]: any}): any | undefined; - getEntityComponent(_0: Entity, _1: {name: string, [key: string]: any}): any | undefined; - addComponent(_0: Entity, _1: {name: string, [key: string]: any}): any | undefined; + getEntityComponentConst(_0: Entity, _1: Component): any | undefined; + getEntityComponent(_0: Entity, _1: Component): any | undefined; + addComponent(_0: Entity, _1: Component): any | undefined; runSystems(_0: any): void; - getZipper(_0: any): any; } interface EmbindModule { container: { - new(): container; + new (): container; }; SparseArray: { - new(): SparseArray; + new (): SparseArray; }; Entity: { - new(_0: number): Entity; + new (_0: number): Entity; }; Registry: { - new(): Registry; + new (): Registry; }; } -export type MainModule = WasmModule & typeof RuntimeExports & EmbindModule; -export default function MainModuleFactory (options?: unknown): Promise; +export type MainModule = WasmModule & EmbindModule; +export default function MainModuleFactory(options?: unknown): Promise; diff --git a/packages/ecs/src/ecs-library.ts b/packages/ecs/src/ecs-library.ts index da308662..3ecd9ee3 100644 --- a/packages/ecs/src/ecs-library.ts +++ b/packages/ecs/src/ecs-library.ts @@ -6,12 +6,12 @@ import { type InitContext, } from "@nanoforge/common"; -import { type MainModule, Module } from "../lib"; -import { ECSRegistry } from "./ecs-registry"; +import type { MainModule, Registry } from "../lib"; +import { Module } from "../lib"; export class ECSLibrary extends BaseComponentSystemLibrary { private module: MainModule; - private _registry: ECSRegistry; + private _registry: Registry; private readonly path: string = "libecs.wasm"; @@ -29,14 +29,14 @@ export class ECSLibrary extends BaseComponentSystemLibrary { async __init(context: InitContext): Promise { const wasmFile = context.libraries.getAssetManager().library.getAsset(this.path); this.module = await Module({ locateFile: () => wasmFile.path }); - this._registry = new ECSRegistry(new this.module.Registry()); + this._registry = new this.module.Registry(); } async __run(ctx: Context): Promise { this._registry.runSystems(ctx); } - get registry(): ECSRegistry { + get registry(): Registry { return this._registry; } } diff --git a/packages/ecs/src/ecs-registry.ts b/packages/ecs/src/ecs-registry.ts deleted file mode 100644 index 562828d9..00000000 --- a/packages/ecs/src/ecs-registry.ts +++ /dev/null @@ -1,78 +0,0 @@ -import { type Context } from "@nanoforge/common"; - -import type { Entity, Registry, SparseArray } from "../lib"; - -export type Component = { name: string; [key: string]: any }; -export type System = (registry: ECSRegistry, ctx: Context) => void; - -export class ECSRegistry { - private _registry: Registry; - - constructor(registry: Registry) { - this._registry = registry; - } - - addComponent(entity: Entity, component: Component): void { - return this._registry.addComponent(entity, component); - } - - spawnEntity(): Entity { - return this._registry.spawnEntity(); - } - - getComponents(component: Component): SparseArray { - return this._registry.getComponents(component); - } - - removeComponent(entity: Entity, component: Component): void { - return this._registry.removeComponent(entity, component); - } - - getEntityComponent(entity: Entity, component: Component): Component | undefined { - return this._registry.getEntityComponent(entity, component); - } - - getEntityComponentConst(entity: Entity, component: Component): Component | undefined { - return this._registry.getEntityComponentConst(entity, component); - } - - clearEntities(): void { - return this._registry.clearEntities(); - } - - runSystems(ctx: Context): void { - return this._registry.runSystems(ctx); - } - - clearSystems(): void { - return this._registry.clearSystems(); - } - - removeSystem(system: any): void { - return this._registry.removeSystem(system); - } - - registerComponent(component: any): SparseArray { - return this._registry.registerComponent(component); - } - - entityFromIndex(index: number): Entity { - return this._registry.entityFromIndex(index); - } - - killEntity(entity: Entity): void { - return this._registry.killEntity(entity); - } - - maxEntities(): number { - return this._registry.maxEntities(); - } - - addSystem(system: System): void { - return this._registry.addSystem(system as unknown as (registry: Registry, ctx: any) => void); - } - - getZipper(types: [Component, ...Component[]]): [any, ...any[]] { - return this._registry.getZipper(types); - } -} diff --git a/packages/ecs/src/index.ts b/packages/ecs/src/index.ts index c17bf8b7..3504fb0f 100644 --- a/packages/ecs/src/index.ts +++ b/packages/ecs/src/index.ts @@ -1,4 +1,4 @@ import "@lib/libecs.wasm"; export { ECSLibrary } from "./ecs-library"; -export type { ECSRegistry, Component, System } from "./ecs-registry"; +export type { Component, System, Registry } from "../lib"; diff --git a/packages/ecs/test/ecs-library.spec.ts b/packages/ecs/test/ecs-library.spec.ts index 70237122..ea804a44 100644 --- a/packages/ecs/test/ecs-library.spec.ts +++ b/packages/ecs/test/ecs-library.spec.ts @@ -2,9 +2,10 @@ import { AssetManagerLibrary } from "@nanoforge/asset-manager"; import { ClearContext, type IConfigRegistry, InitContext } from "@nanoforge/common"; import { EditableApplicationContext } from "@nanoforge/core/src/common/context/contexts/application.editable-context"; import { EditableLibraryManager } from "@nanoforge/core/src/common/library/manager/library.manager"; -import { type ECSRegistry } from "@nanoforge/ecs"; import { ECSLibrary } from "@nanoforge/ecs/src/ecs-library"; +import { type Registry } from "../lib"; + class Position { name: string = "Position"; constructor( @@ -18,7 +19,7 @@ class Position { describe("ECSLibrary", () => { let ecs: ECSLibrary; - let registry: ECSRegistry; + let registry: Registry; const assetManager = new AssetManagerLibrary(); const libraryManager = new EditableLibraryManager(); const appContext = new EditableApplicationContext(libraryManager); diff --git a/packages/ecs/tsconfig.build.json b/packages/ecs/tsconfig.build.json index 6af7f97c..f5a5c1b9 100644 --- a/packages/ecs/tsconfig.build.json +++ b/packages/ecs/tsconfig.build.json @@ -8,7 +8,7 @@ "@nanoforge/common": ["../common"] } }, - "exclude": ["node_modules", "dist", "test/**/*", "*.spec.ts"], + "exclude": ["node_modules", "dist", "test/**/*", "*.spec.ts", "build"], "references": [ { "path": "../common/tsconfig.build.json" diff --git a/packages/ecs/wasm/Registry.cpp b/packages/ecs/wasm/Registry.cpp index 87f2dbd7..0a79ac00 100644 --- a/packages/ecs/wasm/Registry.cpp +++ b/packages/ecs/wasm/Registry.cpp @@ -22,8 +22,10 @@ namespace nfo { EMSCRIPTEN_BINDINGS(Registry) { - emscripten::register_type("{name: string, [key: string]: any}"); - emscripten::register_type("(registry: Registry, ctx: any) => void"); + emscripten::register_type("Component", "{name: string, [key: string]: any}"); + emscripten::register_type("System", "(registry: Registry, ctx: Context) => void"); + emscripten::register_type("Component[]"); + emscripten::register_type("any[]"); emscripten::class_("Registry") .constructor() diff --git a/packages/ecs/wasm/Registry.hpp b/packages/ecs/wasm/Registry.hpp index e3f45cda..566df826 100644 --- a/packages/ecs/wasm/Registry.hpp +++ b/packages/ecs/wasm/Registry.hpp @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -179,7 +180,7 @@ namespace nfo { return _next_entity; } - emscripten::val get_zipper(const emscripten::val &comps) + ZipperOutput get_zipper(const ZipperInput &comps) { if (!comps.isArray()) throw std::runtime_error("getZipper: need an array of comps as parameter"); @@ -206,7 +207,7 @@ namespace nfo { if (need_to_add) arr.set(idx, obj); } - return arr; + return ZipperOutput(arr); } private: diff --git a/packages/ecs/wasm/Utils.hpp b/packages/ecs/wasm/Utils.hpp index 1b7bd097..e8ae8f74 100644 --- a/packages/ecs/wasm/Utils.hpp +++ b/packages/ecs/wasm/Utils.hpp @@ -21,6 +21,8 @@ EMSCRIPTEN_DECLARE_VAL_TYPE(Component); EMSCRIPTEN_DECLARE_VAL_TYPE(ComponentArray); +EMSCRIPTEN_DECLARE_VAL_TYPE(ZipperInput); +EMSCRIPTEN_DECLARE_VAL_TYPE(ZipperOutput); std::optional json_to_str(const emscripten::val &c); std::optional get_js_member(const emscripten::val &c, const std::string &member);