Skip to content

Field Entries

GMalvestiti edited this page Aug 31, 2026 · 2 revisions

@Entry describes one field's on-disk name, comment, restart behavior, and whether a server sends it to its clients. @Config(comment = ...) does the same for the file header.

@Config(name = "mymod", comment = "Settings for MyMod.")
public final class MyModConfig {

    @Entry(name = "hud_scale", comment = "Scale of the HUD overlay, 1 to 4.")
    public int hudScale = 2;

    @Entry(restart = true, comment = "Takes effect on the next launch.")
    public String worldPreset = "default";

    @Entry(comment = {"Enable the particle overlay.", "Disable on low-end hardware."})
    public boolean particleOverlay = true;
}

name decouples the file key from the Java field name. Empty by default (uses the Java name). Set it to keep snake_case on disk, to stabilize a key across a Java rename, or to use a key that is not a legal Java identifier.

comment becomes the text above the entry. One array element per line; a blank element is a blank comment line; an element containing newlines is split, so a text block works just as well as an array. Leading whitespace in each line is preserved. Write the text, not the markers — each format renders them its own way:

// JSON5: single line → // comment, multiple lines → /* block */
@Entry(comment = "Single line.")
@Entry(comment = {"Line one.", "Line two."})

// TOML: each line gets its own # prefix

A */ in comment text is defused to * / so it can never truncate the JSON5 block.

restart marks a field that is read only at game startup. Any update that changes the value of a restart field is rejected as a whole — none of the other fields in the same mutator are applied either:

UpdateResult result = holder.update(config -> config.worldPreset = "flat");
result.accepted();                       // false
result.violations().getFirst().id();     // "restart.worldPreset"

To restore a config file's declared defaults, delete it and restart the game; the next holder initialization recreates it. The restart check follows nested config objects; it does not descend into collections or maps.

translationKey records the key a settings screen should use as the field's label. Lite Config exposes it through ConfigHolder#metadata() but does not resolve it, so it works with any translation system.

sync sends the field from the server to connected clients. Off by default, and additive: a field of a config that already syncs travels either way, so this is how a config that otherwise keeps to itself shares a value or two. Marking a nested object sends everything it holds. See Synchronization.

callback names a method on the field's declaring object that reacts after Lite Config publishes a real change:

@Entry(callback = "applyHudScale")
public int hudScale = 2;

private void applyHudScale(Integer oldValue, Integer newValue, boolean fromSync) {
    hudRenderer.setScale(newValue);
}

The signature is exact: void method(BoxedFieldType oldValue, BoxedFieldType newValue, boolean fromSync). Primitives use their wrapper type (Integer for int), and generic fields use their erased type. Private and inherited methods work. Lite Config validates this while it builds the holder, reporting INVALID_ENTRY_ON_SET_CALLBACK before a malformed method can run.

The method runs only when the value changed structurally and the state transition succeeded. It therefore sees accepted update and load operations, plus values supplied by or reverted after server sync; fromSync is true for those networking transitions. A rejected update and a no-op never call it. The state is already visible when the method runs, so use callbacks for side effects rather than mutating the config object. A callback exception is logged like a lifecycle listener failure and does not roll back the accepted state or stop later callbacks.

For config-wide normalization and validation, see Validation and Lifecycle Hooks. For operation-level notifications, see Lifecycle Listeners.


@Ignore excludes a public field from persistence entirely. The field never appears in the file, is never read back, is never commented, and is never checked by the restart guard. Its value is whatever the constructor sets it to after every load:

@Config(name = "mymod")
public final class MyModConfig {

    public int hudScale = 2;               // persisted normally

    @Ignore
    public String sessionCache = "";       // public but never written to disk
}

Use @Ignore when the field must be public but should never appear in the config file.

Fields and nested sections

Lite Config persists every non-static, non-transient field unless it has @Ignore. Fields may be inherited and do not need to be public when reflective access is available, although public fields keep config models straightforward across Java module boundaries. Persisted fields cannot be final, because loading replaces their values.

A plain object becomes a nested section:

@Config(name = "mymod")
public final class MyModConfig {
    public Hud hud = new Hud();

    public static final class Hud {
        public int scale = 2;
        public boolean compact = false;
    }
}

Use a static nested class with a no-argument constructor. A class annotated with @Config is a file root and cannot be used as a field inside another @Config root. Arrays, enums, lists, sets, maps, and values with registered codecs are stored as leaf values rather than nested sections.

Clone this wiki locally