The build reply
A plugin that sets "build": true returns asset ops from build, and the launcher applies them with the profile's other ops.
A build hook in full
The template writes the items that are on to a file in cache_dir, and puts it into the game:
"launcher": {
"program": { "windows": "bin/example-helper.exe", "linux": "bin/example-helper" },
"build": true
}fn build(&self, host: &mut Host) -> Result<Value, String> {
let on = self.enabled(host)?;
let text = on.join("\n");
let file = self.cache_dir.join("enabled.txt");
// rewrite only on a change
if std::fs::read_to_string(&file).ok().as_deref() != Some(text.as_str()) {
std::fs::write(&file, &text).map_err(|e| e.to_string())?;
}
Ok(json!({
"ops": [{
"op": "file.put",
"source": { "root": "cache", "path": "enabled.txt" }, // cache_dir/enabled.txt
"target": "example-launcher-addon/enabled.txt" // a path in the game folder
}],
"lines": [format!("{} items on", on.len())]
}))
}launcher {"id":4,"method":"build","params":{"options":{"size":24},"features":{"stable":["group","erp","db","lang","file"],"experimental":[]}}}
plugin {"id":0,"method":"get_state","params":null}
launcher {"id":0,"result":{"on":["okayama.exmod"]}}
plugin {"id":4,"result":{"ops":[{"op":"file.put","source":{"root":"cache","path":"enabled.txt"},"target":"example-launcher-addon/enabled.txt"}],"lines":["1 items on"]}}The reply
{
"ops": [ { "op": "file.put", "source": { "root": "cache", "path": "enabled.txt" }, "target": "example-launcher-addon/enabled.txt" } ],
"items": [ "okayama" ],
"lines": ["1 item on"],
"warnings": [],
"fields": { "entrants.teams.team": ["_metadata_", "stat_id", "display_name_code"] }
}Every key may be left out.
ops
- Write each op exactly as you would in an op file: set the
opfield to<kind>.<name>, such asfile.put,db.set,erp.patch-pkg,lang.setorwarn. - For a
sourceinside the addon folder, use a relative path ("art/okayama.dds"). Use{ "root": "data" | "cache", "path": "…" }to read fromdata_dirorcache_dir. - The launcher tags every op with this addon's id as its origin, routes them like declared ops, and reports conflicts by origin (Asset API).
- The build calls
buildfor each enabled addon in load order, after that addon's declared ops. - How these ops reach the game while it boots: Changes at runtime.
features
build.params.features lists the op kinds the installed loader supports, stable and experimental: group, erp, db, lang, file. Check them before returning ops, and drop any op the loader cannot apply.
{ "options": { "size": 24 }, "features": { "stable": ["file"], "experimental": [] } }options holds the profile's value for every option your addon declares (Settings).
lines and warnings
The build prints lines and warnings with the addon id as a prefix.
items
When your addon loads several items, such as the mods in a library, items names the item each op comes from, by position in ops, with null for an op that belongs to no item:
{ "ops": [ { "op": "file.put", … }, { "op": "file.put", … }, { "op": "lang.set", … } ],
"items": [ "okayama", "okayama", null ] }- The launcher adds the item to the op's origin, so each item gets its own runtime overlay archive and conflicts name it.
- Leave
itemsout, or shorter thanops, and the remaining ops have no item.
fields
Launcher 1.0.12. The field names a db.add-row copies, in the same shape as an op file's fields: an object keyed by <table>.<row>, each holding a list of field names.
{
"ops": [ { "op": "db.add-row", "table": "entrants.teams",
"from": "entrants.teams.team[_metadata_=mclaren_26]",
"set": { "_metadata_": "team12_26" } } ],
"fields": { "entrants.teams.team": ["_metadata_", "stat_id", "display_name_code"] }
}fieldsapplies to everydb.add-rowof your addon, from the reply and from your op files.- An op file's own
fieldstakes precedence for the tables it lists. - An
add-rowwhose table is in neither fails withfield names unknown. - A launcher before 1.0.12 ignores
fields: set"compat": { "launcher": ">=1.0.12" }when you returnadd-rowops.