Asset cache
Usage guide of asset.cache: keeping the game files on the player’s device between visits, stores and fallbacks, quota and eviction, settings, services, events and hooks.
Asset cache (asset.cache) #
What it is for #
Keeps the game’s files on the player’s device so a second visit downloads nothing that did not change. In a page it works down a chain of stores the game allows; entries are keyed by path and SHA-256, so a new version of a file is another key and the old one is removed. It is one of three kits that work together: see the asset loading guide for the loading plan and the passports.
In a page the stores are tried in this order: IndexedDB; else Cache Storage with a service worker scoped to the game, which serves the kept files to every request of the game; else the browser HTTP cache, driven by the headers of your servers; else nothing (each file is still downloaded once by asset.preload). Outside a page, the native player reads its files from disk and the kit keeps its decisions on an in-memory store.
Turn it on #
gripforge_gamekit_install { project_id: "gkp_…", id: "asset.cache" }
gripforge_gamekit_configure { project_id: "gkp_…", id: "asset.cache", config: { "quotaMB": 256, "eviction": "lru" } }
First example #
<!-- test: kits/game_engine/host/tests/parity.rs::rust_host_matches_the_typescript_traces (fixture asset.cache.hooks) --> A game refuses to keep an intro video (a hook document) and a mod sets minBytes to 3 and another refuses music. Writing four files keeps only assets/hero.glb: the checksum is compared without regard to case, so reading ABC2 finds it.
{ "id": "intro_fresh", "hook": "asset.cache.store", "match": { "path": "assets/intro.webm" }, "cancel": true, "reason": "fresh at every visit" }
Settings and defaults #
| Setting | Default | Meaning |
|---|---|---|
enabled | true | Off: nothing is kept; every visit downloads the files (once each) |
quotaMB | 512 | Megabytes the store may hold; past it the entries read longest ago leave first (0 keeps nothing) |
version | 1 | Generation of the store: changing it empties the store at the next visit |
store | "auto" | auto walks the chain of fallbacks in a page, memory elsewhere; a store name (indexeddb, cachestorage, http, memory) forces it; off keeps nothing |
fallbacks | ["indexeddb","cachestorage","http"] | The stores a page may use, in order; none left means no cache (each file still downloaded once) |
eviction | "lru" | Which entries leave at the quota: lru (read longest ago), fifo (written first), none (a full store keeps nothing more) |
serviceWorker | true | A service worker scoped to the game folder serves the kept files to every request of the game, by checksum only |
minBytes | 0 | Files smaller than this many bytes are not kept |
A game or a mod may change enabled, quotaMB, fallbacks, eviction and minBytes; version, store and serviceWorker belong to how the game is delivered.
Services #
stats(): the mode in use (indexeddb,cachestorage,http,memory,off),entries,bytes,quota,hits,misses,writes,evicted, and thereasonandtriedstores when one was refused.entries(): the kept files as{ key, path, sha, bytes, used }.write(path, sha, bytes): keeps a file (an empty checksum or non-byte data is refused; the hook below may refuse it too); answers whether it was kept.read(path, sha): the bytes of a kept file, or null when absent or when the checksum differs (a read counts as a use for the eviction order).invalidate(path)removes every entry of a path;clear()empties the store.
Variables: asset.cache.mode (the store in use), asset.cache.bytes and asset.cache.entries, for a debug panel or a loading screen.
Events #
| Name | Detail |
|---|---|
asset.cache.opened | The cache is open: payload { mode, tried, quota, eviction }, the store in use and the ones refused before it. |
asset.cache.hit | A file was read from the store, so nothing was downloaded; payload { path, sha, bytes, mode }. |
asset.cache.miss | A file is not in the store or cannot be read, so it is downloaded; payload { path, sha, reason, mode } with reason one of absent, damaged, no_checksum, http, off, refused. |
asset.cache.stored | A downloaded and checked file was kept; payload { path, sha, bytes, mode }. |
asset.cache.evicted | Entries left to make room; payload { keys, bytes, reason } (reason quota). |
asset.cache.invalidated | The entries of a file were removed, because a new checksum replaced them or invalidate(path) was called; payload { path, keys }. |
asset.cache.unavailable | The store cannot be used; payload { reason, error } with reason refused (private browsing or blocked storage), quota (the browser’s quota is reached) or disabled (the settings). The game goes on without a cache. |
Hooks and modding #
A game or a mod taps a hook with a document of its hooks list, without touching the kit:
asset.cache.store: before a downloaded, checked file is kept; payload{ path, sha, bytes }; cancel to refuse it (a file wanted fresh at every visit, a file too large for this game).asset.cache.evict: before an entry leaves to make room, in the order of the eviction policy; payload{ key, path, bytes, used, policy }; cancel to keep it, and the next entry is asked (a file the game keeps whatever happens).
When it does not do what you expect #
- Nothing is ever a hit: the checksum is missing (
no_checksum), the store isoffordisabled, or the browser blocks storage (unavailablewithrefused, as in private browsing); checkstats(). - Everything is downloaded again after a release: the
versionsetting changed, which empties the store at the next visit.
Limits #
- It stores files by checksum: a file without a SHA-256 is never kept.
- The native player keeps nothing on disk through this kit; it only applies the same decisions (keys, quota, order of eviction, the store hook) on a store in memory.
Versions #
1.0.0 (2026-10-11): first release. Requires game.engine; used by asset.preload.