diff --git a/assets/testscene.js b/assets/testscene.js index fa10809b..694745e4 100644 --- a/assets/testscene.js +++ b/assets/testscene.js @@ -1,8 +1,8 @@ var scene = {}; -// var assets = AssetBatch([ -// { path: 'test.png', type: Asset.TYPE_TEXTURE, format: Texture.FORMAT_RGBA } -// ]); +var assets = AssetBatch([ + { path: 'test.png', type: Asset.TYPE_TEXTURE, format: Texture.FORMAT_RGBA } +]); var cam; var camPos; @@ -11,12 +11,12 @@ var testPos; var testRenderable; var texEntry; -scene.init = function() { - // assets.lock(); - // await assets.loaded(); +scene.init = async function() { + assets.lock(); + await assets.loaded(); Console.print('Scene Init'); - // texEntry = assets.entry(0); + texEntry = assets.entry(0); // Camera at (3, 3, 3) looking at origin cam = Entity.create(); @@ -30,11 +30,11 @@ scene.init = function() { testPos = testEntity.add(Component.POSITION); testRenderable = testEntity.add(Component.RENDERABLE); - // testRenderable.texture = texEntry.texture; - // testRenderable.type = Renderable.SPRITEBATCH; - // testRenderable.sprites = [ - // [0, 0, 1, 1, 0, 1, 1, 0] - // ]; + testRenderable.texture = texEntry.texture; + testRenderable.type = Renderable.SPRITEBATCH; + testRenderable.sprites = [ + [0, 0, 1, 1, 0, 1, 1, 0] + ]; // testPos.localPosition = new Vec3(0, 0, 0); } @@ -42,7 +42,7 @@ scene.dispose = function() { Console.print('Scene Dispose'); Entity.dispose(cam); Entity.dispose(testEntity); - // assets.unlock(); + assets.unlock(); }; module.exports = scene; diff --git a/src/dusk/script/module/asset/moduleassetbatch.c b/src/dusk/script/module/asset/moduleassetbatch.c index 82573e54..b4118d2d 100644 --- a/src/dusk/script/module/asset/moduleassetbatch.c +++ b/src/dusk/script/module/asset/moduleassetbatch.c @@ -253,6 +253,7 @@ moduleBaseFunction(moduleAssetBatchLock) { moduleBaseFunction(moduleAssetBatchUnlock) { jsassetbatch_t *b = moduleAssetBatchSelf(callInfo); if(!b || !b->batch) return jerry_undefined(); + assetBatchUnlock(b->batch); assetBatchDispose(b->batch); memoryFree(b->batch); b->batch = NULL; diff --git a/src/dusk/script/script.c b/src/dusk/script/script.c index e4d2c1dc..a39359ee 100644 --- a/src/dusk/script/script.c +++ b/src/dusk/script/script.c @@ -128,6 +128,12 @@ errorret_t scriptDispose(void) { "All script assets should be disposed by now." ); + // Script modules are now freed, so any JS objects that were only reachable + // through module-scope globals (texEntry, renderable._tex, etc.) are now + // orphaned. A GC pass here fires their finalizers and releases asset entry + // locks before assetDispose() checks ref counts. + jerry_heap_gc(JERRY_GC_PRESSURE_HIGH); + moduleListDispose(); jerry_cleanup(); SCRIPT.initialized = false; diff --git a/types/asset/assetbatch.d.ts b/types/asset/assetbatch.d.ts index 144a9165..8fd8b34c 100644 --- a/types/asset/assetbatch.d.ts +++ b/types/asset/assetbatch.d.ts @@ -31,7 +31,12 @@ interface AssetBatch { /** `true` if any entry is in an `ERROR` state. */ readonly hasError: boolean; /** - * Blocks until every entry is loaded. + * Returns a Promise that resolves when all entries have loaded, or rejects + * if any entry errors. Use with `await`. + */ + loaded(): Promise; + /** + * Blocks (spin-waits) until every entry is loaded. * Returns `this` for chaining. * @throws If any entry fails to load. */ diff --git a/types/asset/assetentry.d.ts b/types/asset/assetentry.d.ts index 2bfe5e78..f20b8d64 100644 --- a/types/asset/assetentry.d.ts +++ b/types/asset/assetentry.d.ts @@ -33,7 +33,12 @@ interface AssetEntry { /** Event proxy — subscribe up to 4 callbacks for when loading fails. */ readonly onError: AssetEventProxy; /** - * Blocks until the entry reaches `LOADED` (or `ERROR`). + * Returns a Promise that resolves when the entry is loaded, or rejects on + * error. Use with `await`. + */ + loaded(): Promise; + /** + * Blocks (spin-waits) until the entry reaches `LOADED` (or `ERROR`). * Returns `this` for chaining. * @throws If the load fails. */ diff --git a/types/asset/asseteventproxy.d.ts b/types/asset/asseteventproxy.d.ts new file mode 100644 index 00000000..32a73501 --- /dev/null +++ b/types/asset/asseteventproxy.d.ts @@ -0,0 +1,23 @@ +/** + * Copyright (c) 2026 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +/** + * An event proxy with up to 4 subscribable callback slots (indices 0–3). + * Assign a function to subscribe; assign `null` to unsubscribe. + * + * @example + * assets.onLoaded[0] = () => { Console.print('all loaded'); }; + * assets.onLoaded[0] = null; // unsubscribe + */ +interface AssetEventProxy { + 0: (() => void) | null; + 1: (() => void) | null; + 2: (() => void) | null; + 3: (() => void) | null; + /** Number of available slots (always 4). */ + readonly length: number; +} diff --git a/types/entity/component.d.ts b/types/entity/component.d.ts index 2820532c..5f71005b 100644 --- a/types/entity/component.d.ts +++ b/types/entity/component.d.ts @@ -19,19 +19,11 @@ interface Component { } interface ComponentConstructor { - /** Sentinel for an invalid component ID. */ - readonly INVALID: number; - - readonly POSITION: number; - readonly CAMERA: number; + readonly POSITION: number; + readonly CAMERA: number; readonly RENDERABLE: number; - readonly PHYSICS: number; - readonly TRIGGER: number; - readonly OVERWORLD: number; - readonly PLAYER: number; - readonly INTERACTABLE: number; - readonly OVERWORLD_CAMERA: number; - readonly OVERWORLD_TRIGGER: number; + readonly PHYSICS: number; + readonly TRIGGER: number; new(): never; } diff --git a/types/index.d.ts b/types/index.d.ts index e185886b..1c40d176 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -26,6 +26,7 @@ /// // asset +/// /// /// /// @@ -41,12 +42,7 @@ // entity / components /// /// -/// -/// -/// -/// /// -/// /// /// ///