Log inGet started
World

Builtin Library

Branchmain
Branchesmainpublish
Commit44fd9e59bd

README.md

toolbox (asset type)

A toolbox is an asset grouping set of <tool>.tool assets around one area of work. Each tool in it becomes callable under the toolbox <toolbox>.<tool>, and is a shell command, zero <toolbox> <tool> <args>; guides core/tools, "A tool's command line", covers its flags, paths, render.luau and exit status.

When to group tools together

When creating tools for the engine, a common theme should be identified and and a toolbox created to house them, this allows each individual operation to be easily accessible instead of being scattered in a million places.

Sharing functionality between tools

A toolbox features ability to define functions that code inside each individual tool inside it can access, this is usefull when establishing helpers etc so you dont have to duplicate code for each new tool you write.

A shared.module inside the toolbox root is where this code lives, each tool inside a toolbox can access its functions by requiring it with require(".shared").

-- <toolbox>.toolbox/shared.module/init.luau
local M = {}

-- One step a tool reports in its result.
export type Step = { name: string, ok: boolean, detail: string? }

--!desc Check that a numeric argument lies within bounds.
--!arg arg The argument's name, for the error.
--!arg value The value passed.
--!arg min The lowest value allowed.
--!arg max The highest value allowed.
--!return The value; raises naming the argument and the range when it lies outside.
--!example "count", 12, 1, 100
typed function M.range(arg: string, value: number, min: number, max: number): number
    if value < min or value > max then
        error(string.format("%s must be between %s and %s, got %s", arg, tostring(min), tostring(max), tostring(value)))
    end
    return value
end

--!desc A byte count as a readable size.
--!arg bytes The count.
--!return The size in B, KB, MB or GB.
--!example 2681107
typed function M.size(bytes: number): string
    local units = { "B", "KB", "MB", "GB" }
    local i = 1
    while bytes >= 1024 and i < #units do
        bytes /= 1024
        i += 1
    end
    return if i == 1 then string.format("%d %s", bytes, units[i]) else string.format("%.1f %s", bytes, units[i])
end

--!desc A step for a tool's result.
--!arg name What the step did.
--!arg ok Whether it succeeded.
--!arg detail What happened, when there is more to say.
--!return The step.
--!example "copy textures", true
typed function M.step(name: string, ok: boolean, detail: string?): Step
    return { name = name, ok = ok, detail = detail }
end

--!desc The asset of `kind` that a caller named, looked up by that name.
--!arg kind The asset type to look in.
--!arg name The name the caller passed.
--!return The asset; raises naming the kind and name when nothing matches.
--!example "material", "gold"
typed function M.named(kind: string, name: string): AssetRef
    local ref = asset.tryResolve(name, kind)
    if ref == nil then
        error(string.format("no %s named '%s'", kind, name))
    end
    return ref
end

return M

This shared code should be limited to being used by the tools inside it, dont put general functionality you might need otuside the toolbox into its shared module. Instead reference those in the shared module should you wish to utilize something.

A benefit the shared module shares with the tools own code is that it allows the author to write code that has computed references, meaning eg resolving an asset from a computed variable asset.resolve(<type>,name) that is normally not allowed in other parts of the engine surface, due to said references not being recorded.

This is usefully for utilizing tools to handle operations that are called by users/agents and are dynamic in nature, such as finding/inspecting/testing assets, or content based on an input etc.

When writing a shared module, you should follow standard module practices, please see module.assetType readme.md for more indepth information.

Describing the toolbox

When writing the readme for your toolbox, ensure you focus on the overall area what the toolbox focuses on, rather than listing individual tools inside it, discovery surfcaces already group tools and toolboxes together so a toolbox can be discoverd by the tools it hosts and doesnt require separately maintaining a list of the tools in it's readme.

What are tools and when do i create ones?

Tools are typed operations with a known serialized output format, made to enable efficient operation in the engine and provide an easily callable interface for operations around a topic.

They are also useful in that they can be called from a multitude of surfaces, from execute, to bash to tool_exec to users Tools portion in the edui. A toolbox and its tools differ from eg a module that has multiple callable functionm in that they might return anything, and come with no guarantee that the output is meant to be inspected directly, where as tools are meant to perform an self contained operation and return results/info/relevant data in a readable format, including eg. image data.

Whenever you identify an operation that requires specific orchestration and or is something that could be a repeated operation and makes it easier to manage the project/task or when you want to expose logic written in luau to the other call surfaces like bash under its zero command,you should reach for creating a toolbox.

Toolboxes also come in handy for limiting control/inputs, exposing a limited set of functionality in a clean manner or ensuring the caller doesnt have to understand the entire code.

For comparison, you could think of toolboxes as programs, and the tools inside them as commands. A package might contain tens of tousands of lines of code, dozens of modules, components etc, a toolbox turns that surface into a system that any agent/user can then utilize without having to read or understand the code inside.

Its the UI equilevant to operations.

For the indepth explanation of what tool.assetType is and does, read it's readme.