3.8 KiB
RFC 0008 — Runtime mod option schema export
Status
Proposed. Engine: src/mods/Loader.lua. Tests:
tests/mod_loader_tests.lua. This RFC defines an optional filesystem
contract; it does not require a native launcher or any other consumer.
Motivation
A native launcher may want to present settings for installed mods before it starts the game. Running every mod's entry chunk in that launcher just to discover its settings would duplicate engine behavior and give the launcher an unnecessary code-execution surface. The engine already has the authoritative runtime schemas after mod loading, so it can publish a data-only snapshot for platform shells that want one.
The exact contract
After the mod loader has finished running entry chunks, it may write
mod_option_schemas.json beside options.lua in the same filesystem. The
document is a snapshot of the current boot; it is not a second settings store
and does not change how option values are read or written.
Version 1 has this shape:
{
"schema_version": 1,
"mods": {
"example": [
{"key":"enabled","type":"toggle","label":"Enabled","default":true},
{"key":"mode","type":"choice","label":"Mode","default":"safe",
"choices":[["Safe","safe"],["Fast","fast"]]},
{"key":"rate","type":"number","label":"Rate","default":5,
"min":0,"max":10,"step":1},
{"key":"name","type":"text","label":"Name","default":"","maxLen":12}
]
}
}
mods is keyed by mod id. Its rows come from the runtime
mod.options:define schema, or from the legacy manifest options_schema file
when the runtime schema is absent. The supported row types are toggle,
choice, number, and text. Their optional fields retain the meanings
established by the existing in-game option UI: choices are [label, value]
pairs, numeric rows may provide min, max, and step, and text rows may
provide maxLen.
Only mods that are enabled and successfully loaded in the current boot are
included. A disabled or failed mod must not contribute rows. If an older
snapshot exists and the current boot has no schema-bearing mods, the producer
overwrites it with {"schema_version":1,"mods":{}}; this prevents stale
settings rows from surviving a disable or load failure. A fresh mod-free boot
does not create the file, and a filesystem without write support is tolerated.
The producer writes the snapshot after entry chunks and the final load set have been established. Consumers must treat the file as untrusted input and must not execute anything from it.
Compatibility and versioning
The contract is optional on both sides. A native consumer may be absent, and the engine continues normally if the file cannot be written. A native consumer is not required to render, validate, or persist every supported row; it may ignore an unknown row type or optional field.
For compatibility with files produced by the original unversioned prototype,
a missing schema_version means version 1. Consumers must ignore documents
with a newer version rather than guessing at their shape. Producers must bump
the version whenever they change the document shape or the meaning of an
existing field. Version 1 is therefore the legacy unversioned format as well
as the explicitly versioned format shown above.
Migration note
Nothing. Existing mods, option values, and the in-game options UI are
unchanged. Platforms that do not consume mod_option_schemas.json have no
new integration requirement.
Parity tests
tests/mod_loader_tests.lua verifies the explicit version, runtime and legacy
row round-tripping, enabled/disabled filtering, failed-mod filtering,
stale-snapshot clearing, and tolerance of a read-only filesystem.
Deprecation etiquette
Nothing is deprecated. The unversioned file form remains readable as legacy
version 1; new producers write the explicit schema_version field.