Skip to content

Holder Implementation

GMalvestiti edited this page Aug 31, 2026 · 3 revisions

Every holder exposes one ConfigHolder API. Choose a mutable or read-only holder when building it, then choose synchronous or asynchronous execution for each operation.

Builder call Behavior Best fit
create() Allows loads and updates Regular runtime config
readOnly().create() Refuses loads and updates Config a mod reads but never changes
ConfigHolder<MyModConfig> config = LiteConfig.holder(MyModConfig.class)
    .modId("mymod")
    .create();

config.update(values -> values.hudScale = 3);       // runs on the calling thread
config.saveAsync();                                  // queues work on the config worker

ConfigHolder<MyModConfig> readOnly = LiteConfig.holder(MyModConfig.class)
    .modId("mymod")
    .readOnly()
    .create();

Execution: every lifecycle operation is serialized on the config's worker lane. load, save, update, and updateAndSave wait for completion; their *Async counterparts return the queued operation's CompletableFuture. Starting another config operation from a mutator or lifecycle hook is rejected as NESTED_CONFIG_OPERATION; compose follow-up work after the current call or future.

Read-only: update and load are refused through the matching failure policy — they throw under STRICT. Under FALLBACK, load logs and skips the operation, while updates return a rejected UpdateResult. save still writes the current state. The config itself is not frozen: it is shared with every other holder of the same class, so a value another holder changes is visible through data() here too.

One config, one state

A config is registered once per config directory. Building a second holder for the same class hands back another handle onto that registration, so both agree on the values and both see each other's changes:

ConfigHolder<MyModConfig> hud = LiteConfig.holder(MyModConfig.class).modId("mymod").create();
ConfigHolder<MyModConfig> commands = LiteConfig.holder(MyModConfig.class).modId("mymod").create();

hud.updateAndSave(config -> config.hudScale = 3);

commands.data().hudScale; // 3

The first build reads the file and resolves the options; a later build reuses that registration rather than re-reading or reconfiguring it. Each holder can add its own listeners. Call close() when a holder is no longer needed; this removes listeners registered through that holder and releases the shared registration after its last holder closes. data() and metadata() remain readable on a closed holder, while lifecycle and mutation operations are rejected. Every holder exposes one ConfigHolder API. Choose a mutable or read-only holder when building it, then choose synchronous or asynchronous execution for each operation.

Builder call Behavior Best fit
create() Allows loads and updates Regular runtime config
readOnly().create() Refuses loads and updates Config a mod reads but never changes
ConfigHolder<MyModConfig> config = LiteConfig.holder(MyModConfig.class)
    .modId("mymod")
    .create();

config.update(values -> values.hudScale = 3);       // runs on the calling thread
config.saveAsync();                                  // queues work on the config worker

ConfigHolder<MyModConfig> readOnly = LiteConfig.holder(MyModConfig.class)
    .modId("mymod")
    .readOnly()
    .create();

Execution: every lifecycle operation is serialized on the config's worker lane. load, save, update, and updateAndSave wait for completion; their *Async counterparts return the queued operation's CompletableFuture. Starting another config operation from a mutator or lifecycle hook is rejected as NESTED_CONFIG_OPERATION; compose follow-up work after the current call or future.

Read-only: update and load are refused through the matching failure policy — they throw under STRICT. Under FALLBACK, load logs and skips the operation, while updates return a rejected UpdateResult. save still writes the current state. The config itself is not frozen: it is shared with every other holder of the same class, so a value another holder changes is visible through data() here too.

Clone this wiki locally