Files
gen1recomp/docs/mod-option-schema.md
2026-08-10 20:23:01 -07:00

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.