Binary templates
A template can be compiled ahead of time to a small binary file, .xmlui. Loading it skips the XML parser and the expression parser at start-up, and a broken template is caught when you compile rather than when a player opens the screen.
When to use it
| Use XML text when | Use binary when |
|---|---|
| You are still designing the screen | The screen is finished and ships with the game |
| You want hot reload | Start-up time matters, or there are many templates |
| The template is generated at run time | You want compile errors at build time |
The two forms build identical widgets. Keep the .xml as the source and treat the .xmlui as a build output.
Compile with xmluic
willowui offers the xmluic tool. From a project that lists willowui as a package:
sh
thicket tool xmluic compile ui/settings.xml ui/settings.xmluithicket tool xmluic ui/settings.xml ui/settings.xmlui (without compile) does the same. A template error prints its line and column and the tool exits with a failure code.
The tool knows the built-in tags. For a template that uses custom widgets, list them in a widget manifest, a JSON file that maps each tag to the module that defines it, and pass it with --widgets:
sh
thicket tool xmluic compile --widgets ui/widgets.json ui/rating.xml ui/rating.xmluijson
{
"StarRating": { "import": "./star_rating.tks", "attributes": { "maxStars": "int" } }
}Each entry needs an import path. attributes is optional and maps an attribute name to a type name. A bare string ("StarRating": "./star_rating.tks") is the short form, and the entries may also sit inside a top-level "widgets" object. The tool only reads the manifest to learn the tag names; it does not load your widget code. At run time, still call registerWidget before you load the .xmlui.
Load it
ts
import { Files } from "fs";
const bytes: Buffer = await Files.readFile(moduleDir + "/ui/settings.xmlui");
reg.registerBinary("settings", bytes);
const root: Component = reg.instantiate("settings", controller, screen);registerBinary throws if the bytes are not a valid template (template "settings": ...).
The format
For tools that want to read or write it. All numbers are little-endian.
| Part | Content |
|---|---|
| Magic | The four bytes X, U, I, 0x00. |
| Version | u16, currently 1. |
| Node | Component type and text (strings); a u32 attribute count, each attribute as name, static value, expression and an is-event flag; a u32 directive count, each as a type byte, expression and loop variable name; a u32 child count and the child nodes, depth first; then the theme patch (a string, empty for none). |
| Strings | A u32 byte length, then the UTF-8 bytes. |
| Expressions | Stored as their parsed tree, so no expression is parsed at load time. Literal numbers are stored as 64-bit floats. |
encodeIR(node) and decodeIR(bytes) are the public encoder and decoder, and compileXML(text) gives the IRNode tree to encode, so you can build your own pipeline step.