hafen.asset: The Collection
hafen.asset() is the collection of the files this addon has loaded. It adds the one read that looks at your folder rather than at what is loaded. Every verb is unprotected. Every path is addon-relative and sandboxed.
for _, path in ipairs(hafen.asset():files("songs")) do
hafen.log():write(path .. ": " .. hafen.asset():get(path):info().bytes .. " bytes")
end
The collection
| Method |
Returns |
Permission |
Description |
hafen.asset():get(path) |
asset |
Unprotected |
Load and intern one file, typed by its extension. |
hafen.asset():list(filter) |
asset[] |
Unprotected |
This addon’s live assets, in load order. |
hafen.asset():count(filter) |
number |
Unprotected |
How many, without building the array. |
hafen.asset():find(filter) |
asset \| nil |
Unprotected |
The first that matches. |
hafen.asset():remove(asset) |
the collection |
Unprotected |
Free one now rather than at teardown. |
hafen.asset():files(dir) |
string[] |
Unprotected |
The addon-relative paths of the files inside one folder of yours (below). |
| Rule |
Detail |
filter |
The canonical filter. A string matches the addon-relative path an asset was loaded from. |
No :add |
An asset is a file you shipped. |
:remove(asset) |
Takes the handle. Frees the memory on the spot and drops the intern entry, so the next :get(path) re-reads the file as a new object. Your own files only: a built-in font, a derived variant, a map drawing and a file another addon loaded are each refused, saying which. |
| Only loaded files are listed |
A built-in font has no file, path or lifetime. A derived variant is not listed either. A freed asset is gone and never resurrected. |
for _, asset in ipairs(hafen.asset():list()) do
hafen.log():write(asset:type() .. " " .. asset:path())
end
The files you ship
| Method |
Returns |
Permission |
Description |
hafen.asset():files(dir) |
string[] |
Unprotected |
The regular files directly inside dir, as a 1-based array of addon-relative paths spelled with /, sorted by name. Each is a path :get takes as it is. dir is relative to your addon folder ("songs", "img/icons"). Omitted, it names the addon folder itself. |
local songs = hafen.asset():files("songs") -- { "songs/air.mid", "songs/reel.mid" }
if #songs == 0 then hafen.log():write("no songs shipped") end
| Rule |
Detail |
| Reads the folder every call |
A file dropped in while the client runs is seen on the next call without a :reload. |
Exactly what :get would load |
The containment check resolves dir and every entry: an entry whose real path leaves your folder (a link out) is not listed. A folder inside dir is not listed. Name it to see under it. An empty folder is an empty array. |
| Raises |
A dir that is absolute or climbs out, as :get does. One that is not a folder of yours, naming it. |
Errors
Every refusal is a pcall-able error naming hafen.asset.
| Call |
Refusal names |
:get("/etc/passwd") |
The path is absolute. Every path is relative to your own folder. |
:get("../other/icon.png") |
The path is not inside your addon folder. |
:get("link/icon.png"), link pointing out |
The same refusal: the check follows the link. |
:get("nope.png") |
No such file in this addon’s folder, checked before any decode. |
:get("broken.png") |
Not a decodable image, not a valid font, or the glTF parser’s own message. |
:get(1) |
The key is a path string, not a number. |
:get({}), :get(fn) |
Expected a path string, got a table or a function. |
:get("") |
The path must be a non-empty string. |
:get(nil) |
The key is required: arity is the verb, so a nil variable is refused rather than read as the list. |
:get("icon.png") from :lua |
The console has no addon folder. An asset path is relative to the loading addon’s folder. |
:files("nope") |
No such folder in this addon’s folder. |
:files("../other") |
The path is not inside your addon folder. |
:remove("icon.png") |
Pass the handle, not a path. |
:remove(handle) on a built-in font or a variant |
Neither was loaded from a file, so this collection does not hold it. |
:remove(image) on another addon’s image |
The addon that loaded it: freeing a file is that addon’s job. |
:remove(image) on a map drawing |
A picture of the database, not a file you shipped. It ends with image:dispose(). |
Example
local icon, face, chair -- upvalues; a reload rebuilds the env, so they are nil again
hafen.event():on("Load", function()
icon = hafen.asset():get("icon.png")
face = hafen.asset():get("fonts/Inter.ttf"):derive():size(12)
chair = hafen.asset():get("props/chair.glb")
local icon_size, chair_bounds = icon:size(), chair:bounds()
local tiles_tall = math.floor(chair_bounds.extent.z / 11 * 10 + 0.5) / 10 -- one decimal by hand: %.1f prints the raw double
hafen.log():write(("icon %dx%d, chair %s tiles tall"):format(icon_size.w, icon_size.h, tiles_tall))
end)
local window = hafen.ui():window():title("My addon"):size(160, 80):font(face)
window:on("Draw", function(draw_event)
local graphics = draw_event:g()
graphics:image(icon, 4, 4, 16, 16) -- the handle, not the path
graphics:text("mine", 26, 6)
end)
hafen.console():on("stand", function()
local position = hafen.session():current():player():gob():position()
hafen.virtual():object():add(chair, position) -- the handle, again
end)
See Also
hafen.asset — the door: the types, the sandbox, interning.
- Handles — what each kind of loaded file answers.
hafen.font — the built-ins that are not assets, and why they are not listed here.
- Map drawings — the one picture with an asset’s verbs that is not one of your files.