From 2ca678030545641691b021d53630631a644cf125 Mon Sep 17 00:00:00 2001 From: Dominic Masters Date: Mon, 8 Jun 2026 09:39:09 -0500 Subject: [PATCH] Just trying to fix things now --- CLAUDE.md | 13 ++-- assets/player.js | 24 +++++-- assets/testscene.js | 61 ++++-------------- src/dusk/asset/assetbatch.c | 64 +++++++++---------- src/dusk/asset/assetbatch.h | 18 ++++++ src/dusk/scene/scene.js | 4 ++ .../script/module/asset/moduleassetbatch.c | 29 +++++++++ .../script/module/asset/moduleassetbatch.h | 9 +++ types/asset/assetbatch.d.ts | 6 ++ 9 files changed, 137 insertions(+), 91 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index cf585b6a..ed7bea5e 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -53,6 +53,13 @@ All struct and enum types end in `_t` (`animation_t`, `errorret_t`, …). ### Implementation files (`.c`) - Contain function bodies only; no declarations. - Pull in whatever additional includes the implementation needs. +- Do not use `static` or `inline` on **functions**. Every function, + including internal helpers, must be declared in the matching `.h` and + defined in the `.c` file. Internal helpers belong near the bottom of + the `.c` file, not at the top with a `static` qualifier. + `static` and `inline` on functions are only appropriate when the + function body is written directly inside a `.h` file. + `static` on **variables** (file-scope state) is fine and expected. ### Formatting - Hard-wrap all lines at **80 characters**. @@ -398,10 +405,8 @@ const size_t size ``` ### Comments in `.c` files -- Block comments that describe a section use the divider style: - ```c - /* ---- Public API ---- */ - ``` +- Do not use section dividers (`/* ---- ... ---- */`). Just let the + functions follow one another with a single blank line between them. - Multi-line explanatory comments inside function bodies use `//` lines: ```c // Script modules are freed; orphaned JS wrapper objects now get GC'd diff --git a/assets/player.js b/assets/player.js index ed6b1c19..3582b60c 100644 --- a/assets/player.js +++ b/assets/player.js @@ -4,12 +4,24 @@ // https://opensource.org/licenses/MIT const PLAYER_SPEED = 5.0; +// 1 world unit = 16 pixels. +const PIXEL_SCALE = 1.0 / 16.0; +// Player sprite is 32x32 px (test.png dimensions). +const PLAYER_W = 32 * PIXEL_SCALE; +const PLAYER_H = 32 * PIXEL_SCALE; var player = {}; -var _entity, _position, _physics; +player.getAssets = () => { + return [ + { path: 'test.png', type: Asset.TYPE_TEXTURE, format: Texture.FORMAT_RGBA } + ]; +} + +player.init = function(scene) { + var texture = scene.assets.getAssetByPath('test.png'); + Console.print('Player init: got texture ' + texture); -player.create = function(texEntry) { _entity = Entity.create(); _position = _entity.add(Component.POSITION); _physics = _entity.add(Component.PHYSICS); @@ -19,13 +31,13 @@ player.create = function(texEntry) { _physics.gravityScale = 1.0; var r = _entity.add(Component.RENDERABLE); - r.texture = texEntry.texture; + r.texture = texture.texture; r.type = Renderable.SPRITEBATCH; r.color = new Color(220, 80, 80); - // upright quad: (-0.5,0,0) -> (0.5,1,0) in XY plane - r.sprites = [[-0.5, 0, 0, 0.5, 1, 0, 0, 1, 1, 0]]; + // Upright quad centered on X, bottom-aligned on Y. + r.sprites = [[-PLAYER_W/2, 0, 0, PLAYER_W/2, PLAYER_H, 0, 0, 1, 1, 0]]; - _position.localPosition = new Vec3(0, 1, 0); + _position.localPosition = new Vec3(0, PLAYER_H, 0); }; player.getPosition = function() { diff --git a/assets/testscene.js b/assets/testscene.js index be324abb..9999e85e 100644 --- a/assets/testscene.js +++ b/assets/testscene.js @@ -4,11 +4,6 @@ // https://opensource.org/licenses/MIT var scene = {}; -var player = require('player.js'); - -var assets = AssetBatch([ - { path: 'test.png', type: Asset.TYPE_TEXTURE, format: Texture.FORMAT_RGBA } -]); // Pokemon DS-style camera: ~34 degrees elevation (atan(6/9)). // CAM_HEIGHT / CAM_DIST ratio controls the tilt - keep it under 0.7 for @@ -16,58 +11,30 @@ var assets = AssetBatch([ const CAM_HEIGHT = 6; const CAM_DIST = 9; -var cam, camPos; -var floorEntity; - -function updateCamera() { - var pp = player.getPosition().worldPosition; - // Position is offset above and behind the player; lookAt the exact player - // world position so the player projects to the center pixel every frame. - camPos.localPosition = new Vec3(pp.x, pp.y + CAM_HEIGHT, pp.z + CAM_DIST); - camPos.lookAt(new Vec3(pp.x, pp.y, pp.z)); -} - scene.init = async function() { - assets.lock(); - await assets.loaded(); - - var texEntry = assets.entry(0); - // Camera - cam = Entity.create(); - camPos = cam.add(Component.POSITION); - cam.add(Component.CAMERA); + scene.cam = Entity.create(); + var camPos = scene.cam.add(Component.POSITION); + var cam = scene.cam.add(Component.CAMERA); + camPos.localPosition = new Vec3(3, 3, 3); + camPos.lookAt(new Vec3(0, 0, 0)); - // Floor - infinite static plane at Y=0. Rendered as a large flat blue - // slab using the default SHADER_MATERIAL (no texture needed). - floorEntity = Entity.create(); - var floorPos = floorEntity.add(Component.POSITION); - var floorPhysics = floorEntity.add(Component.PHYSICS); - floorPhysics.bodyType = Physics.STATIC; - floorPhysics.shape = Physics.SHAPE_PLANE; - - var floorR = floorEntity.add(Component.RENDERABLE); + // Floor - large flat slab, no texture needed. + scene.floor = Entity.create(); + var floorPos = scene.floor.add(Component.POSITION); + var floorR = scene.floor.add(Component.RENDERABLE); + floorR.type = Renderable.SHADER_MATERIAL; floorR.color = Color.BLUE; - floorPos.localScale = new Vec3(16, 0.2, 16); - floorPos.localPosition = new Vec3(0, -0.1, 0); - - // Player - spawns 1 unit above the floor so physics drops it cleanly. - player.create(texEntry); - - // Initialise camera at the correct angle from the player's spawn position. - updateCamera(); + // floorPos.localScale = new Vec3(16, 0.2, 16); + // floorPos.localPosition = new Vec3(0, -0.1, 0); }; scene.update = function() { - player.update(); - updateCamera(); }; scene.dispose = function() { - player.dispose(); - Entity.dispose(floorEntity); - Entity.dispose(cam); - assets.unlock(); + Entity.dispose(scene.floor); + Entity.dispose(scene.cam); }; module.exports = scene; diff --git a/src/dusk/asset/assetbatch.c b/src/dusk/asset/assetbatch.c index 1bb1345a..3bb27927 100644 --- a/src/dusk/asset/assetbatch.c +++ b/src/dusk/asset/assetbatch.c @@ -11,38 +11,6 @@ #include "util/memory.h" #include -/* ---- Per-entry event trampolines ----------------------------------------- */ - -static void assetBatchEntryOnLoadedCb(void *params, void *user) { - assetentry_t *entry = (assetentry_t *)params; - assetbatch_t *batch = (assetbatch_t *)user; - - batch->loadedCount++; - eventInvoke(&batch->onEntryLoaded, entry); - - if((uint16_t)(batch->loadedCount + batch->errorCount) >= batch->count) { - if(batch->errorCount == 0) { - eventInvoke(&batch->onLoaded, batch); - } else { - eventInvoke(&batch->onError, batch); - } - } -} - -static void assetBatchEntryOnErrorCb(void *params, void *user) { - assetentry_t *entry = (assetentry_t *)params; - assetbatch_t *batch = (assetbatch_t *)user; - - batch->errorCount++; - eventInvoke(&batch->onEntryError, entry); - - if((uint16_t)(batch->loadedCount + batch->errorCount) >= batch->count) { - eventInvoke(&batch->onError, batch); - } -} - -/* ---- Public API ---------------------------------------------------------- */ - void assetBatchInit( assetbatch_t *batch, const uint16_t count, @@ -86,7 +54,7 @@ void assetBatchInit( ); if(batch->entries[i]->state == ASSET_ENTRY_STATE_LOADED) { - /* Already loaded (cached) - count it now, no subscription needed. */ + // Already loaded (cached) - count it now, no subscription needed. batch->loadedCount++; } else if(batch->entries[i]->state == ASSET_ENTRY_STATE_ERROR) { batch->errorCount++; @@ -161,9 +129,37 @@ void assetBatchDispose(assetbatch_t *batch) { if(batch->entries[i]) { // Unsubscribe while we still hold a lock so the entry is live. eventUnsubscribe(&batch->entries[i]->onLoaded, assetBatchEntryOnLoadedCb); - eventUnsubscribe(&batch->entries[i]->onError, assetBatchEntryOnErrorCb); + eventUnsubscribe(&batch->entries[i]->onError, assetBatchEntryOnErrorCb); assetUnlockEntry(batch->entries[i]); } } memoryZero(batch, sizeof(assetbatch_t)); } + +void assetBatchEntryOnLoadedCb(void *params, void *user) { + assetentry_t *entry = (assetentry_t *)params; + assetbatch_t *batch = (assetbatch_t *)user; + + batch->loadedCount++; + eventInvoke(&batch->onEntryLoaded, entry); + + if((uint16_t)(batch->loadedCount + batch->errorCount) >= batch->count) { + if(batch->errorCount == 0) { + eventInvoke(&batch->onLoaded, batch); + } else { + eventInvoke(&batch->onError, batch); + } + } +} + +void assetBatchEntryOnErrorCb(void *params, void *user) { + assetentry_t *entry = (assetentry_t *)params; + assetbatch_t *batch = (assetbatch_t *)user; + + batch->errorCount++; + eventInvoke(&batch->onEntryError, entry); + + if((uint16_t)(batch->loadedCount + batch->errorCount) >= batch->count) { + eventInvoke(&batch->onError, batch); + } +} diff --git a/src/dusk/asset/assetbatch.h b/src/dusk/asset/assetbatch.h index 7b2dd6ac..20aaac2e 100644 --- a/src/dusk/asset/assetbatch.h +++ b/src/dusk/asset/assetbatch.h @@ -104,3 +104,21 @@ errorret_t assetBatchRequireLoaded(assetbatch_t *batch); * @param batch Batch to dispose. */ void assetBatchDispose(assetbatch_t *batch); + +/** + * Event trampoline invoked when a batch entry finishes loading. + * Increments the loaded counter and fires batch-level events. + * + * @param params The loaded assetentry_t pointer. + * @param user The owning assetbatch_t pointer. + */ +void assetBatchEntryOnLoadedCb(void *params, void *user); + +/** + * Event trampoline invoked when a batch entry fails to load. + * Increments the error counter and fires batch-level events. + * + * @param params The errored assetentry_t pointer. + * @param user The owning assetbatch_t pointer. + */ +void assetBatchEntryOnErrorCb(void *params, void *user); diff --git a/src/dusk/scene/scene.js b/src/dusk/scene/scene.js index 5e6710be..c133b448 100644 --- a/src/dusk/scene/scene.js +++ b/src/dusk/scene/scene.js @@ -7,9 +7,13 @@ var Scene = { }; Scene.update = () => { + if(!Scene.current || !Scene.current.update) return; + Scene.current.update(); } Scene.dynamicUpdate = () => { + if(!Scene.current || !Scene.current.dynamicUpdate) return; + Scene.current.dynamicUpdate(); } Scene.set = (newScene) => { diff --git a/src/dusk/script/module/asset/moduleassetbatch.c b/src/dusk/script/module/asset/moduleassetbatch.c index b4118d2d..314ec864 100644 --- a/src/dusk/script/module/asset/moduleassetbatch.c +++ b/src/dusk/script/module/asset/moduleassetbatch.c @@ -6,6 +6,7 @@ */ #include "moduleassetbatch.h" +#include "util/string.h" #define ASSET_BATCH_LOADED_MAX 8 @@ -273,6 +274,30 @@ moduleBaseFunction(moduleAssetBatchEntry) { return scriptProtoCreateValue(&MODULE_ASSET_ENTRY_PROTO, &e); } +moduleBaseFunction(moduleAssetBatchGetAssetByPath) { + moduleBaseRequireArgs(1); + jsassetbatch_t *b = moduleAssetBatchSelf(callInfo); + if(!b || !b->batch) return jerry_undefined(); + if(!jerry_value_is_string(args[0])) return jerry_undefined(); + + char_t path[ASSET_FILE_NAME_MAX]; + jerry_size_t pathLen = jerry_string_to_buffer( + args[0], JERRY_ENCODING_UTF8, + (jerry_char_t *)path, ASSET_FILE_NAME_MAX - 1 + ); + path[pathLen] = '\0'; + + for(uint16_t i = 0; i < b->batch->count; i++) { + assetentry_t *entry = b->batch->entries[i]; + if(!entry) continue; + if(!stringEquals(entry->name, path)) continue; + assetEntryLock(entry); + jsassetentry_t e = { .entry = entry }; + return scriptProtoCreateValue(&MODULE_ASSET_ENTRY_PROTO, &e); + } + return jerry_undefined(); +} + moduleBaseFunction(moduleAssetBatchGetOnLoaded) { jsassetbatch_t *b = moduleAssetBatchSelf(callInfo); if(!b || !b->batch) return jerry_undefined(); @@ -349,6 +374,10 @@ void moduleAssetBatchInit(void) { scriptProtoDefineFunc( &MODULE_ASSET_BATCH_PROTO, "entry", moduleAssetBatchEntry ); + scriptProtoDefineFunc( + &MODULE_ASSET_BATCH_PROTO, "getAssetByPath", + moduleAssetBatchGetAssetByPath + ); scriptProtoDefineProp( &MODULE_ASSET_BATCH_PROTO, "onLoaded", moduleAssetBatchGetOnLoaded, NULL diff --git a/src/dusk/script/module/asset/moduleassetbatch.h b/src/dusk/script/module/asset/moduleassetbatch.h index 799739b5..c7d57ac2 100644 --- a/src/dusk/script/module/asset/moduleassetbatch.h +++ b/src/dusk/script/module/asset/moduleassetbatch.h @@ -89,6 +89,15 @@ moduleBaseFunction(moduleAssetBatchUnlock); */ moduleBaseFunction(moduleAssetBatchEntry); +/** + * getAssetByPath(path) - finds the first entry whose name matches path + * and returns it as a locked AssetEntry, or undefined if not found. + * The returned entry must be unlocked separately when no longer needed. + * @param args[0] Path string to match against entry names. + * @return AssetEntry, or undefined if no entry matches. + */ +moduleBaseFunction(moduleAssetBatchGetAssetByPath); + /** @return The onLoaded Event (fires once when all entries load). */ moduleBaseFunction(moduleAssetBatchGetOnLoaded); diff --git a/types/asset/assetbatch.d.ts b/types/asset/assetbatch.d.ts index 8fd8b34c..09b23a7b 100644 --- a/types/asset/assetbatch.d.ts +++ b/types/asset/assetbatch.d.ts @@ -57,6 +57,12 @@ interface AssetBatch { * Returns `undefined` if `index` is out of range or the batch is disposed. */ entry(index: number): AssetEntry | undefined; + /** + * Returns the first `AssetEntry` whose path matches, adding an + * independent lock. Returns `undefined` if no entry matches. + * The returned entry must be unlocked separately when no longer needed. + */ + getAssetByPath(path: string): AssetEntry | undefined; /** * Fires once when every entry has loaded successfully.