Log inGet started
World

Builtin Library

Branchmain
Branchesmainpublish
Commit3bb1da2647

README.md

vfs_async_read

Pure-Luau wrapper that grafts a yielding vfs.read over the engine's sync vfs.read binding. The vfs API module (modules/api/engine/vfs) calls M.installInto(vfs) on itself as it loads, so every VM holding vfs (user VMs and the trusted VM alike) reads through it; user code never requires this module directly: it just calls vfs.read(path) and the wrapper handles cache misses by yielding the running coroutine until the bytes resolve.

Exports

  • M.installInto(vfs: VfsNamespace): replace vfs.read (and vfs.readBounded and vfs.list) with the yielding wrapper. No-op when the target lacks read and readAsync as functions.
  • M.CANNOT_WAIT: "VFS_READ_CANNOT_WAIT", the code a read raises when it names a known file whose bytes have not arrived and the reading code cannot yield.

Types:

  • VfsNamespace = { read?, readAsync?, readBounded?, list? }: the engine's vfs global shape.

Usage

-- The `vfs` API module installs the wrapper onto itself as it loads:
require("@builtin::modules.vfs_async_read").installInto(vfs)

-- After install, every `vfs.read` call yields on cache miss, and a read
-- that finds no bytes answers nil and the reason:
local bytes, why = vfs.read("/zero/source/some.asset")

Where a read can wait

  • On a miss the wrapper schedules vfs.readAsync(path, opts), which fires BlobProvider::fetch under the hood, and task.awaits the promise. A wait that resolves without bytes answers nil and the reason: the sentence the engine's read gave beside its nil, or, for bytes that were on their way, the one it gives once the wait ends.
  • Waiting takes a yield, so it happens where coroutine.isyieldable() is true: a task, the component hooks the engine runs on a coroutine (awake, start, update, fixedUpdate, onEnable, onOwnerChanged, onSyncReceived), a replicated event's handlers, and a world or scene entrypoint's hooks.
  • Where it is false (onDisable, onDestroy, onPropertyChanged, a received synced-function call, a vfs.watch callback, a module's top level while require runs it, a metamethod, a table.sort comparator, a VM's main thread) a miss the engine's read answers nil, "pending" for (a file vfs.exists knows that is no directory and whose bytes can still arrive, a render surface being captured, a /runtime/sync node whose current reading the next frame assembles) raises VFS_READ_CANNOT_WAIT: vfs.read("<path>") ..., naming the path and the places it can be read from. Every other miss answers nil and the reason there as everywhere else.
  • vfs.list answers a directory whose current listing the next frame assembles (a /runtime/sync directory nothing has read lately) after waiting that frame where the code can yield, and raises VFS_READ_CANNOT_WAIT: vfs.list("<path>") ... where it cannot.
  • installInto is idempotent in the trivial sense: calling it twice re-wraps vfs.read around the already-wrapped function, which is fine but pointless.