diff --git a/packages/common/src/library/libraries/consts/library-options-default.const.ts b/packages/common/src/library/libraries/consts/library-options-default.const.ts new file mode 100644 index 0000000..01698c1 --- /dev/null +++ b/packages/common/src/library/libraries/consts/library-options-default.const.ts @@ -0,0 +1,7 @@ +import { type ILibraryOptions } from "../library.type"; + +export const DEFAULT_LIBRARY_OPTIONS: ILibraryOptions = { + dependencies: [], + runBefore: [], + runAfter: [], +}; diff --git a/packages/common/src/library/libraries/library.ts b/packages/common/src/library/libraries/library.ts index 744094a..0d65660 100644 --- a/packages/common/src/library/libraries/library.ts +++ b/packages/common/src/library/libraries/library.ts @@ -1,7 +1,28 @@ import { type ClearContext, type InitContext } from "../../context"; -import { type ILibrary } from "./library.type"; +import { RelationshipHandler } from "../relationship/relationship-handler"; +import { DEFAULT_LIBRARY_OPTIONS } from "./consts/library-options-default.const"; +import { type ILibrary, type ILibraryOptions } from "./library.type"; export abstract class Library implements ILibrary { + protected _relationship: RelationshipHandler; + + constructor(rawOptions?: Partial) { + const options = { + ...DEFAULT_LIBRARY_OPTIONS, + ...rawOptions, + }; + + this._relationship = new RelationshipHandler( + options.dependencies, + options.runBefore, + options.runAfter, + ); + } + + get relationship(): RelationshipHandler { + return this._relationship; + } + abstract get name(): string; // eslint-disable-next-line @typescript-eslint/no-unused-vars diff --git a/packages/common/src/library/libraries/library.type.ts b/packages/common/src/library/libraries/library.type.ts index c9913bc..477040c 100644 --- a/packages/common/src/library/libraries/library.type.ts +++ b/packages/common/src/library/libraries/library.type.ts @@ -1,9 +1,18 @@ import { type ApplicationContext, type ClearContext } from "../../context"; +import { type RelationshipHandler } from "../relationship/relationship-handler"; export interface ILibrary { get name(): string; + get relationship(): RelationshipHandler; + init(context: ApplicationContext): Promise; clear(context: ClearContext): Promise; } + +export interface ILibraryOptions { + dependencies: symbol[]; + runBefore: symbol[]; + runAfter: symbol[]; +} diff --git a/packages/common/src/library/manager/handle/library.handle.ts b/packages/common/src/library/manager/handle/library.handle.ts index ee24361..f9fee46 100644 --- a/packages/common/src/library/manager/handle/library.handle.ts +++ b/packages/common/src/library/manager/handle/library.handle.ts @@ -1,5 +1,5 @@ import { type LibraryContext } from "../../../context"; -import { type ILibrary } from "../../libraries/library.type"; +import { type ILibrary } from "../../libraries"; export class LibraryHandle { private readonly _symbol: symbol; diff --git a/packages/common/src/library/manager/managers/library.manager.ts b/packages/common/src/library/manager/managers/library.manager.ts index 2aa8f01..7939a48 100644 --- a/packages/common/src/library/manager/managers/library.manager.ts +++ b/packages/common/src/library/manager/managers/library.manager.ts @@ -14,17 +14,17 @@ import { BaseLibraryManager } from "./base-library.manager"; export enum DefaultLibrariesEnum { ASSET_MANAGER, - INPUT, COMPONENT_SYSTEM, - NETWORK, GRAPHICS, + INPUT, + NETWORK, } const DEFAULT_LIBRARIES: { index: DefaultLibrariesEnum; sym: symbol }[] = [ { index: DefaultLibrariesEnum.ASSET_MANAGER, sym: ASSET_MANAGER_LIBRARY }, { index: DefaultLibrariesEnum.COMPONENT_SYSTEM, sym: COMPONENT_SYSTEM_LIBRARY }, - { index: DefaultLibrariesEnum.NETWORK, sym: NETWORK_LIBRARY }, { index: DefaultLibrariesEnum.GRAPHICS, sym: GRAPHICS_LIBRARY }, + { index: DefaultLibrariesEnum.NETWORK, sym: NETWORK_LIBRARY }, ]; export class LibraryManager extends BaseLibraryManager { diff --git a/packages/common/src/library/relationship/relationship-handler.ts b/packages/common/src/library/relationship/relationship-handler.ts new file mode 100644 index 0000000..4c5ae31 --- /dev/null +++ b/packages/common/src/library/relationship/relationship-handler.ts @@ -0,0 +1,30 @@ +export class RelationshipHandler { + private readonly _dependencies: symbol[]; + private readonly _runBefore: symbol[]; + private readonly _runAfter: symbol[]; + + /** + * Constructor for RelationshipHandler + * + * @param dependencies - Dependencies of the library + * @param runBefore - Libraries needed to run before this one + * @param runAfter - Libraries needed to run after this one + */ + constructor(dependencies: symbol[], runBefore: symbol[], runAfter: symbol[]) { + this._dependencies = dependencies; + this._runBefore = runBefore; + this._runAfter = runAfter; + } + + get dependencies(): symbol[] { + return this._dependencies; + } + + get runBefore(): symbol[] { + return this._runBefore; + } + + get runAfter(): symbol[] { + return this._runAfter; + } +} diff --git a/packages/core/package.json b/packages/core/package.json index 33895a6..fc13c09 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -31,6 +31,11 @@ "taze": "taze major -w", "lint-staged": "lint-staged" }, + "dependencies": { + "@nanoforge/asset-manager": "workspace:^", + "@nanoforge/common": "workspace:^", + "@nanoforge/input": "workspace:^" + }, "devDependencies": { "@commitlint/cli": "^19.8.0", "@commitlint/config-conventional": "^19.8.0", diff --git a/packages/core/src/application/nanoforge-application.ts b/packages/core/src/application/nanoforge-application.ts index 4684380..0ab3b19 100644 --- a/packages/core/src/application/nanoforge-application.ts +++ b/packages/core/src/application/nanoforge-application.ts @@ -47,8 +47,6 @@ export abstract class NanoforgeApplication { } public run() { - this._core.run().then(() => { - console.info("Game ended successfully."); - }); + this._core.run(); } } diff --git a/packages/core/src/application/nanoforge-factory.ts b/packages/core/src/application/nanoforge-factory.ts index 84711a6..807ec2f 100644 --- a/packages/core/src/application/nanoforge-factory.ts +++ b/packages/core/src/application/nanoforge-factory.ts @@ -1,14 +1,22 @@ +import { AssetManagerLibrary } from "@nanoforge/asset-manager"; +import { InputLibrary } from "@nanoforge/input"; + import { type IApplicationOptions } from "./application-options.type"; import { NanoforgeClient } from "./nanoforge-client"; import { NanoforgeServer } from "./nanoforge-server"; class NanoforgeFactoryStatic { createClient(options?: Partial): NanoforgeClient { - return new NanoforgeClient(options); + const app = new NanoforgeClient(options); + app.useAssetManager(new AssetManagerLibrary()); + app.useInput(new InputLibrary()); + return app; } createServer(options?: Partial): NanoforgeServer { - return new NanoforgeServer(options); + const app = new NanoforgeServer(options); + app.useAssetManager(new AssetManagerLibrary()); + return app; } } diff --git a/packages/core/src/common/library/manager/library.manager.ts b/packages/core/src/common/library/manager/library.manager.ts index 8f6090e..b07e281 100644 --- a/packages/core/src/common/library/manager/library.manager.ts +++ b/packages/core/src/common/library/manager/library.manager.ts @@ -17,6 +17,7 @@ import { } from "@nanoforge/common"; import { EditableLibraryContext } from "../../context/contexts/library.editable-context"; +import { Relationship } from "../relationship-functions"; export class EditableLibraryManager extends LibraryManager { public set(sym: symbol, library: ILibrary) { @@ -58,11 +59,23 @@ export class EditableLibraryManager extends LibraryManager { this._set(DefaultLibrariesEnum.INPUT, INPUT_LIBRARY, library, new EditableLibraryContext()); } - public getLibraries(): LibraryHandle[] { + public getLibraries(): LibraryHandle[] { return this._libraries; } - public getRunnerLibraries(): LibraryHandle[] { + public getInitLibraries(): LibraryHandle[] { + return Relationship.getLibrariesByDependencies(this._libraries); + } + + public getExecutionLibraries(): LibraryHandle[] { + return Relationship.getLibrariesByRun(this._getRunnerLibraries()); + } + + public getClearLibraries(): LibraryHandle[] { + return Relationship.getLibrariesByDependencies(this._libraries, true); + } + + private _getRunnerLibraries(): LibraryHandle[] { return this._libraries.filter( (handle) => handle && typeof handle.library["run"] === "function", ) as LibraryHandle[]; diff --git a/packages/core/src/common/library/relationship-functions.ts b/packages/core/src/common/library/relationship-functions.ts new file mode 100644 index 0000000..1bf1332 --- /dev/null +++ b/packages/core/src/common/library/relationship-functions.ts @@ -0,0 +1,122 @@ +import { type ILibrary, type LibraryHandle } from "@nanoforge/common"; + +class RelationshipStatic { + getLibrariesByDependencies(libraries: LibraryHandle[], reverse: boolean = false) { + let response: LibraryHandle[] = []; + for (const library of libraries) { + if (!library) continue; + response = this._pushLibraryWithDependencies(library, response, [], libraries); + } + + if (reverse) return response.reverse(); + return response; + } + + getLibrariesByRun(libraries: LibraryHandle[]) { + let response: LibraryHandle[] = []; + const dependencies = new Map>( + libraries.map((library) => [library.symbol, new Set()]), + ); + + for (const handle of libraries) { + const key = handle.symbol; + + for (const before of handle.library.relationship.runBefore) { + this._pushToDependencies(key, before, dependencies); + } + for (const after of handle.library.relationship.runAfter) { + this._pushToDependencies(after, key, dependencies); + } + } + + for (const library of libraries) { + response = this._pushLibraryWithDependenciesRun( + library, + dependencies, + response, + [], + libraries, + ); + } + return response; + } + + private _pushToDependencies( + key: symbol, + value: symbol, + dependencies: Map>, + ): void { + let curr = dependencies.get(key); + if (!curr) curr = new Set(); + curr.add(value); + dependencies.set(key, curr); + } + + private _pushLibraryWithDependenciesRun( + handle: LibraryHandle, + dependencies: Map>, + response: LibraryHandle[], + cache: symbol[], + libraries: LibraryHandle[], + ): LibraryHandle[] { + const key = handle.symbol; + if (this._symbolIsInList(key, response)) return response; + + if (cache.includes(key)) throw new Error("Circular dependencies !"); + + cache.push(key); + + const deps = dependencies.get(key); + if (!deps) throw new Error("Dependencies not found"); + + for (const dep of deps) { + if (this._symbolIsInList(dep, response)) continue; + + const depHandle = libraries.find((lib) => lib?.symbol === dep) as LibraryHandle; + if (!depHandle) throw new Error(`Cannot find library ${dep.toString()}`); + + response = this._pushLibraryWithDependenciesRun( + depHandle, + dependencies, + response, + cache, + libraries, + ); + } + cache.pop(); + + response.push(handle); + return response; + } + + private _pushLibraryWithDependencies( + handle: LibraryHandle, + response: LibraryHandle[], + cache: symbol[], + libraries: LibraryHandle[], + ): LibraryHandle[] { + if (this._symbolIsInList(handle.symbol, response)) return response; + + if (cache.includes(handle.symbol)) throw new Error("Circular dependencies !"); + + cache.push(handle.symbol); + for (const dep of handle.library.relationship.dependencies) { + if (this._symbolIsInList(dep, response)) continue; + + const depHandle = libraries.find((lib) => lib?.symbol === dep) as LibraryHandle; + if (!depHandle) throw new Error(`Cannot find library ${dep.toString()}`); + + response = this._pushLibraryWithDependencies(depHandle, response, cache, libraries); + } + cache.pop(); + + response.push(handle); + return response; + } + + private _symbolIsInList(sym: symbol, libraries: LibraryHandle[]): boolean { + return libraries.some((lib) => lib.symbol === sym); + } +} + +export const Relationship = new RelationshipStatic(); diff --git a/packages/core/src/core/core.ts b/packages/core/src/core/core.ts index 6196cd1..a0013a3 100644 --- a/packages/core/src/core/core.ts +++ b/packages/core/src/core/core.ts @@ -6,10 +6,12 @@ import { type IRunnerLibrary, InitContext, type LibraryHandle, + LibraryStatusEnum, } from "@nanoforge/common"; import { type ApplicationConfig } from "../application/application-config"; import type { IApplicationOptions } from "../application/application-options.type"; +import { type EditableLibraryContext } from "../common/context/contexts/library.editable-context"; export class Core { private readonly config: ApplicationConfig; @@ -28,7 +30,7 @@ export class Core { public async run(): Promise { const context = this.getExecutionContext(); - const libraries = this.config.libraryManager.getRunnerLibraries(); + const libraries = this.config.libraryManager.getExecutionLibraries(); let requestAnimationFrameHandle: number; const runner = async () => { @@ -38,6 +40,7 @@ export class Core { const render = () => { if (!context.isRunning) { clearInterval(intervalHandle); + this.runClear(this.getClearContext()); return; } cancelAnimationFrame(requestAnimationFrameHandle); @@ -60,20 +63,22 @@ export class Core { } private async runInit(context: InitContext): Promise { - for (const handle of this.config.libraryManager.getLibraries()) { - if (handle) await handle.library.init(context); + for (const handle of this.config.libraryManager.getInitLibraries()) { + await handle.library.init(context); + (handle.context as EditableLibraryContext).setStatus(LibraryStatusEnum.LOADED); } } private async runExecute(context: ExecutionContext, libraries: LibraryHandle[]) { for (const handle of libraries) { - if (handle) await handle.library.run(context); + await handle.library.run(context); } } - private runClear(context: ClearContext) { - for (const handle of this.config.libraryManager.getLibraries()) { - if (handle) handle.library.clear(context); + private async runClear(context: ClearContext) { + for (const handle of this.config.libraryManager.getClearLibraries()) { + await handle.library.clear(context); + (handle.context as EditableLibraryContext).setStatus(LibraryStatusEnum.CLEAR); } } } diff --git a/packages/core/tsconfig.build.json b/packages/core/tsconfig.build.json index a7e09c7..08c7f1d 100644 --- a/packages/core/tsconfig.build.json +++ b/packages/core/tsconfig.build.json @@ -11,6 +11,12 @@ "references": [ { "path": "../common/tsconfig.build.json" + }, + { + "path": "../asset-manager/tsconfig.build.json" + }, + { + "path": "../input/tsconfig.build.json" } ] } diff --git a/packages/ecs/src/ecs-library.ts b/packages/ecs/src/ecs-library.ts index d697b38..cee7384 100644 --- a/packages/ecs/src/ecs-library.ts +++ b/packages/ecs/src/ecs-library.ts @@ -1,5 +1,10 @@ import { type AssetManagerLibrary } from "@nanoforge/asset-manager"; -import { BaseComponentSystemLibrary, type InitContext } from "@nanoforge/common"; +import { + ASSET_MANAGER_LIBRARY, + BaseComponentSystemLibrary, + GRAPHICS_LIBRARY, + type InitContext, +} from "@nanoforge/common"; import type { Entity, MainModule, Registry, SparseArray } from "../lib"; import { Module } from "../lib"; @@ -12,6 +17,13 @@ export class ECSLibrary extends BaseComponentSystemLibrary { private registry: Registry; private readonly path: string = "libecs.wasm"; + constructor() { + super({ + dependencies: [ASSET_MANAGER_LIBRARY], + runAfter: [GRAPHICS_LIBRARY], + }); + } + get name(): string { return "ECSLibrary"; } @@ -28,10 +40,6 @@ export class ECSLibrary extends BaseComponentSystemLibrary { this.runSystems(); } - clear(): Promise { - return Promise.resolve(); - } - addComponent(entity: Entity, component: Component): void { this.registry.addComponent(entity, component); } diff --git a/packages/ecs/test/ecs-library.spec.ts b/packages/ecs/test/ecs-library.spec.ts index 0f13ba7..ecc8440 100644 --- a/packages/ecs/test/ecs-library.spec.ts +++ b/packages/ecs/test/ecs-library.spec.ts @@ -1,5 +1,5 @@ import { AssetManagerLibrary } from "@nanoforge/asset-manager"; -import { ApplicationContext, InitContext } from "@nanoforge/common"; +import { ApplicationContext, ClearContext, InitContext } from "@nanoforge/common"; import { EditableLibraryManager } from "@nanoforge/core/src/common/library/manager/library.manager"; import { ECSLibrary } from "@nanoforge/ecs/src/ecs-library"; @@ -19,7 +19,7 @@ describe("ECSLibrary", () => { const assetManager = new AssetManagerLibrary(); const appContext = new ApplicationContext(); const libraryManager = new EditableLibraryManager(); - const context = new InitContext(appContext, libraryManager, { + const initContext = new InitContext(appContext, libraryManager, { // @ts-ignore canvas: null, files: { @@ -28,15 +28,17 @@ describe("ECSLibrary", () => { wgsl: new Map(), }, }); + + const clearContext = new ClearContext(appContext, libraryManager); libraryManager.setAssetManager(assetManager); beforeAll(async () => { - await assetManager.init(context); + await assetManager.init(initContext); }); beforeEach(async () => { ecs = new ECSLibrary(); - await ecs.init(context); + await ecs.init(initContext); }); test("init and spawn entity", async () => { @@ -55,6 +57,6 @@ describe("ECSLibrary", () => { }); test("clear", async () => { - await ecs.clear(); + await ecs.clear(clearContext); }); }); diff --git a/packages/graphics-2d/src/components/component.ts b/packages/graphics-2d/src/components/component.ts index 23e4f97..6c06698 100644 --- a/packages/graphics-2d/src/components/component.ts +++ b/packages/graphics-2d/src/components/component.ts @@ -63,7 +63,7 @@ export abstract class NfgComponent { this._core.initContext.canvas.width, this._core.initContext.canvas.height, ]); - console.log(this._core.initContext.canvas.width, this._core.initContext.canvas.height); + if (!this._uniformBuffer) this._uniformBuffer = this._core.device.createBuffer({ label: "View Uniforms", diff --git a/packages/graphics-2d/src/graphics-2d.library.ts b/packages/graphics-2d/src/graphics-2d.library.ts index 7be4d75..8406d88 100644 --- a/packages/graphics-2d/src/graphics-2d.library.ts +++ b/packages/graphics-2d/src/graphics-2d.library.ts @@ -1,4 +1,4 @@ -import { BaseGraphicsLibrary, type InitContext } from "@nanoforge/common"; +import { ASSET_MANAGER_LIBRARY, BaseGraphicsLibrary, type InitContext } from "@nanoforge/common"; import { GraphicsCore } from "./core"; import { GraphicsFactory } from "./factory"; @@ -8,6 +8,12 @@ export class Graphics2DLibrary extends BaseGraphicsLibrary { private _core: GraphicsCore; private _factory: GraphicsFactory; + constructor() { + super({ + dependencies: [ASSET_MANAGER_LIBRARY], + }); + } + get name(): string { return "Graphics2DLibrary"; } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8624570..935fc29 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -162,6 +162,16 @@ importers: version: 8.26.1(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2) packages/core: + dependencies: + '@nanoforge/asset-manager': + specifier: workspace:^ + version: link:../asset-manager + '@nanoforge/common': + specifier: workspace:^ + version: link:../common + '@nanoforge/input': + specifier: workspace:^ + version: link:../input devDependencies: '@commitlint/cli': specifier: ^19.8.0