Skip to content

Configs

MehVahdJukaar edited this page Jul 25, 2026 · 1 revision

Configs

One config builder that produces a NeoForge spec or a Fabric json, and gives you a config screen for free.

If you've used Forge's ModConfigSpec this will look familiar. What it adds:

  • Any object with a codec as a config value, not just primitives. Lists and maps of them too.
  • Declared schemas. Hand it a codecui SchemaCodec instead of a plain Codec and the screen builds a real form for the object instead of a JSON text box.
  • Beans. Point it at a record or a POJO and it defines one option per field.
  • Rich value types that store the same but edit better: colors, sliders, percentages, ranges, vectors, regex, dropdowns, registry pickers with item previews, list editors with autocomplete.
  • Feature toggles that compose, so a nested feature reads false when its parent is off.
  • Server sync. A COMMON_SYNCED config is pushed to clients on join, server values win.
  • Change hints: mark a value as needing a world reload, a game restart, or as invalidating the dynamic asset cache.
  • Comments that are also tooltips, registered as lang entries so they can be translated later.

Getting Started

public static final Supplier<Boolean> SOME_TOGGLE;
public static final Supplier<Integer> COLOR;
public static final Supplier<MyObj> OBJECT;
public static final ModConfigHolder CONFIG;

static {
    ConfigBuilder builder = ConfigBuilder.create(MyMod.MOD_ID, ConfigType.COMMON_SYNCED);

    builder.push("misc");
    SOME_TOGGLE = builder.comment("This is a boolean config").define("some_toggle", true);
    COLOR = builder.comment("Hex color config").defineColor("color", 0xff0000);
    OBJECT = builder.defineObject("custom_object", () -> new MyObj(2, 4), MyObj.CODEC);
    builder.pop();

    CONFIG = builder.build();
    CONFIG.forceLoad();   // optional, loads the file right now
}

Read with SOME_TOGGLE.get(). The file lands in the config folder as <modid>-<common|client>.toml on NeoForge, .json on Fabric.

ConfigType is COMMON, COMMON_SYNCED or CLIENT.

Example: ConfigBuilderHelper

Schema objects

A plain codec gets a raw JSON editor on the screen. A SchemaCodec gets a generated form:

public static final SchemaCodec<MyObj> SCHEMA_CODEC = SchemaRecord.create(MyObj.class, i -> i.group(
        i.field("first", SchemaCodecs.intRange(0, 100), MyObj::first),
        i.field("second", SchemaCodecs.intRange(0, 100), MyObj::second)
).apply(i, MyObj::new));

SCHEMA_CONFIG = builder.defineObject("schema_object", () -> new MyObj(2, 4), SCHEMA_CODEC);

A SchemaCodec is a Codec, so the file format is identical. The only difference is that the screen can draw two sliders instead of a text box.

Feature toggles

For mods that gate content behind config. The returned supplier is already ANDed with every enclosing feature:

builder.push("redstone");
Supplier<Boolean> redstone = builder.mainFeature();             // this category's own "enabled"
Supplier<Boolean> speaker  = builder.feature("speaker_block");  // false when redstone is off
builder.pop();

Nothing is rewritten on disk when you flip a parent, composition happens at read time. On the screen they draw as ✓/✗ switches with an icon. You can also ask by name later: CONFIG.isFeatureEnabled("redstone.speaker_block").

Change hints

builder.worldReload().define("some_worldgen_thing", true);
builder.gameRestart().define("some_startup_thing", true);
builder.affectsDynamicPacks().define("generated_texture_style", "fancy");

The first two show a warning on the screen. The third invalidates the generated resource cache when the value changes.

Clone this wiki locally