AssetManager

AssetManager is a singleton that handles loading, caching, and lifecycle management of game assets. It supports GLTF models, textures, audio, JSON, and binary data with concurrency-limited loading, priority ordering, LRU cache eviction, and retry with exponential backoff.

Unlike other managers, AssetManager is not mounted automatically by Game. Use AssetLoader for declarative preloading, or access the manager directly for imperative control.

tsx
import { getAssetManager, destroyAssetManager, detectAssetType } from "@carverjs/core/systems";

How It Works #

  1. Manifest parsing: Asset entries are normalized — relative URLs are resolved against baseUrl, types are auto-detected from file extensions, and entries are sorted by priority.

  2. Concurrency-limited loading: A worker pool pattern loads assets in parallel (default: 6 concurrent). Each worker pulls the next task from a shared queue.

  3. Retry with backoff: Failed loads retry up to 3 times with exponential backoff and jitter to avoid thundering herd.

  4. Caching: Loaded assets are stored in an in-memory LRU cache (256 MB default). Reference counting prevents eviction of assets in active use.

  5. Progress tracking: Real-time progress is available via useSyncExternalStore (used by useAssetProgress).

  6. Resource disposal: On eviction or explicit unload, Three.js resources (textures, geometries, materials) are properly disposed.


API #

getAssetManager() #

Returns the singleton AssetManager instance. Creates one if it doesn't exist.

destroyAssetManager() #

Clears all caches, disposes Three.js resources, and destroys the singleton.

detectAssetType(url) #

Detect asset type from a URL's file extension. Returns AssetType | undefined.


Loading #

MethodDescription
load<T>(url, options?)Load a single asset. Returns cached result if available. Deduplicates concurrent requests
loadManifest(manifest, callbacks?)Load all non-lazy assets from a manifest with progress tracking
loadGroup(name)Load all assets in a named group
preload(urls)Fire-and-forget preload for one or more URLs

load() Options #

OptionTypeDescription
typeAssetTypeOverride auto-detection
keystringCache key (defaults to URL)
loaderOptionsRecord<string, unknown>Passed to the underlying loader

loadManifest() Callbacks #

CallbackDescription
onProgress(progress)Called on each progress update
onComplete()Called when all assets finish loading
onError(errors)Called when any asset fails after all retries

Cache Access #

MethodDescription
get<T>(key)Get a cached asset synchronously. Returns undefined if not cached
has(key)Check if an asset is cached
isLoading(key)Check if an asset is currently loading

Cache Management #

MethodDescription
unload(key)Unload a specific asset, disposing Three.js resources
unloadGroup(group)Unload all assets in a group
clearAll()Clear all cached assets
getMemoryUsage()Get current cache size in bytes
retain(key)Increment reference count (prevents LRU eviction)
release(key)Decrement reference count

Configuration #

tsx
const manager = getAssetManager();
manager.configure({
  maxConcurrent: 8,      // Parallel loads (default: 6)
  maxCacheBytes: 512 * 1024 * 1024, // Cache limit (default: 256 MB)
  retries: 5,            // Retry attempts (default: 3)
  retryDelay: 2000,      // Base retry delay in ms (default: 1000)
  timeout: 60000,        // Per-asset timeout in ms (default: 30000)
});

Custom Loaders #

Register custom loaders for new asset types or override built-in ones:

tsx
const manager = getAssetManager();

manager.registerLoader("shader", async (url, options) => {
  const response = await fetch(url);
  return response.text();
});

// Now loadable:
const shader = await manager.load("/shaders/glow.glsl", { type: "shader" });

Built-in Loaders #

TypeLoaderOutput
"gltf"GLTFLoader (three-stdlib, lazy-imported)GLTF scene object
"texture"TextureLoader (three, lazy-imported)THREE.Texture
"audio"fetcharrayBuffer()ArrayBuffer
"json"fetchjson()Parsed object
"binary"fetcharrayBuffer()ArrayBuffer

GLTF supports Draco and Meshopt decompression via loaderOptions:

tsx
await manager.load("/model.glb", {
  type: "gltf",
  loaderOptions: { draco: true, meshopt: false },
});

Usage #

Most game code should use AssetLoader + useAssets. Direct manager access is useful for imperative scenarios:

tsx
import { getAssetManager } from "@carverjs/core/systems";

// Preload assets before a level transition
async function preloadLevel(level: number) {
  const manager = getAssetManager();
  await manager.loadManifest([
    { url: `/maps/level-${level}.json` },
    { url: `/audio/level-${level}.mp3` },
  ]);
}

// Access cached data
function getLevelConfig(level: number) {
  return getAssetManager().get(`/maps/level-${level}.json`);
}

Memory Management #

The cache uses LRU (Least Recently Used) eviction with reference counting:


Type Definitions #

See Types for AssetType, AssetEntry, AssetManifest, AssetGroupConfig, LoadingProgress, AssetLoadError, and CacheEntry.