Files
dusk/.claude/assets.md
T
2026-06-16 10:15:59 -05:00

4.1 KiB

Asset System

Source: src/dusk/asset/

Overview

All game assets are packed into a single ZIP archive named dusk.dsk (ASSET_FILE_NAME). The asset system loads entries from this archive asynchronously on a background thread, caches them, and provides synchronous blocking access when an asset is required immediately.

Key limits

Constant Value Meaning
ASSET_LOADING_COUNT_MAX 4 Concurrent in-flight loads
ASSET_ENTRY_COUNT_MAX 128 Cached entries

Top-level API (asset.h)

errorret_t assetInit();    // Open dusk.dsk, start background thread
void assetUpdate();        // Dispatch completed-load callbacks (main thread)
errorret_t assetDispose(); // Wait for loads, close archive

assetentry_t *assetGetEntry(
  const char_t *path,
  assetloadertype_t type,
  assetloaderinput_t *input
);  // Get (or create) a cache entry; does NOT start loading

errorret_t assetRequireLoaded(assetentry_t *entry);
// Block the calling thread until this entry is fully loaded.
// Only safe to call from the main thread.

void assetLock(assetentry_t *entry);
void assetUnlock(assetentry_t *entry);
// Reference counting. Lock before using loaded data; unlock when done.
// The entry will not be evicted while locked.

Asset entry states

Each cache entry goes through a state machine:

IDLE -> QUEUED -> READING (async) -> PROCESSING (sync, main thread) -> LOADED
                                                                     -> ERROR
  • READING runs on the background loader thread (file I/O).
  • PROCESSING runs on the main thread (GPU uploads, parsing finalization).
  • Once LOADED, data is available in entry->data.

Loader types

Loader types are registered in the ASSET_LOADER_CALLBACKS[] table. Each type implements three callbacks: loadSync, loadAsync, dispose.

assetloadertype_t Data read Description
ASSET_LOADER_TYPE_TEXTURE STB image Loads image bytes async, creates GPU texture sync
ASSET_LOADER_TYPE_TILESET .dtf binary Custom tile format (magic, version, grid, UVs)
ASSET_LOADER_TYPE_MESH .stl STL mesh with configurable axis orientation
ASSET_LOADER_TYPE_JSON yyjson Up to 256 KB; parsed async
ASSET_LOADER_TYPE_LOCALE Gettext .po PO parser with plural-form expression evaluation
ASSET_LOADER_TYPE_SCRIPT JS source JerryScript module

Adding a new loader type

  1. Add an enum value before _COUNT in assetloadertype_t (src/dusk/asset/loader/assetloader.h).
  2. Add fields to the input/loading/output unions in assetloader.h.
  3. Implement assetXxxLoaderSync, assetXxxLoaderAsync, and assetXxxDispose in src/dusk/asset/loader/xxx/.
  4. Register the three callbacks in ASSET_LOADER_CALLBACKS[] in src/dusk/asset/loader/assetloader.c.
  5. If user-facing, create a JS module and a .d.ts file (see CLAUDE.md).

Asset batch (assetbatch.h)

assetbatch_t groups multiple asset requests into a single logical load. All entries in the batch start loading concurrently. The batch fires a completion callback once every entry has reached LOADED (or ERROR).

assetbatch_t batch;
assetBatchInit(&batch, entries, count, onComplete, user);
assetBatchStart(&batch);
// ... later, after assetUpdate() fires the callback ...
assetBatchDispose(&batch);

Usage pattern

// 1. Get or create the cache entry (no I/O yet).
assetentry_t *tex = assetGetEntry(
  "textures/hero.png",
  ASSET_LOADER_TYPE_TEXTURE,
  NULL
);
assetLock(tex);

// 2. Option A -- non-blocking: check tex->state each frame.
// Option B -- blocking (main thread only):
errorChain(assetRequireLoaded(tex));

// 3. Use the loaded data.
texture_t *t = &tex->data.texture;

// 4. Release when done.
assetUnlock(tex);

Error macros (inside loader implementations)

assetLoaderErrorThrow("msg %d", val);  // errorThrow equivalent
assetLoaderErrorChain(someCall());     // errorChain equivalent

Use these instead of the bare error macros inside loader callbacks so that failures include the loader context in the stack trace.