Fzzy Config

Documentation

Getting started

Add the dependency, write a class, register it once.

Fzzy Config turns a plain Java or Kotlin class into a TOML file and a full in-game settings screen. You write the class; the library handles serialization, the GUI, validation and server-client sync. There is no screen builder to implement.

Add the dependency

Fzzy Config is published on the author's own maven. The artifact is me.fzzyhmstrs:fzzy_config, with an underscore rather than a hyphen, which is the single most common setup mistake.

repositories {
    maven {
        name = "FzzyMaven"
        url = uri("https://maven.fzzyhmstrs.me/")
    }
    // Forge and NeoForge only
    maven { url = uri("https://thedarkcolour.github.io/KotlinForForge/") }
}

dependencies {
    modImplementation("me.fzzyhmstrs:fzzy_config:$fzzyConfigVersion")
}

Version names follow x.y.z+mcversion, so 0.7.6+1.21.3 is Fzzy Config 0.7.6 built for Minecraft 1.21.3. Forge and NeoForge builds add a suffix: +forge on 1.20.1, +neoforge from 1.20.4 onward.

Write a config class

A config extends Config and takes an Identifier. Fields must be public, non-final and non-static. Wrap a field in a Validated type when you want bounds, choices or suggestions; leave it plain when you do not care, and it still gets type checking.

class MyConfig : Config(Identifier.of(MOD_ID, "my_config")) {

    // plain field: still type-checked on load
    var enableFeature = true

    // bounded 0.0 to 10.0, corrected if the file says otherwise
    var speed = ValidatedDouble(5.0, 10.0, 0.0)

    // its own screen, with a breadcrumb back to the parent
    var advanced = AdvancedSection()

    class AdvancedSection : ConfigSection() {
        @RequiresAction(Action.RESTART)
        var renderDistance = ValidatedInt(8, 32, 2)
    }
}

Kotlin users: a config cannot be an object, because it has to be instanced. Keep a separate object holding the instance if you want global access.

Register it

One call reads the file, creates it if missing, and registers both the screen and the sync. Call it from a static or object initialiser so it runs before anything asks for the values.

object Configs {
    // BOTH by default: client GUI plus server sync
    val CONFIG = ConfigApi.registerAndLoadConfig(::MyConfig)

    // client only: a GUI, no syncing
    val CLIENT = ConfigApi.registerAndLoadConfig(::MyClientConfig, RegisterType.CLIENT)

    // server only: syncing, no GUI
    val SERVER = ConfigApi.registerAndLoadConfig(::MyServerConfig, RegisterType.SERVER)
}

In Java, use ConfigApiJava instead of ConfigApi. It exists to avoid awkward Kotlin interop in the signatures.

public class Configs {
    public static MyConfig CONFIG =
        ConfigApiJava.registerAndLoadConfig(MyConfig::new);

    public static MyConfig CLIENT =
        ConfigApiJava.registerAndLoadConfig(MyClientConfig::new, RegisterType.CLIENT);
}

RegisterType

ValueGUISyncUse it for
BOTHYesYesThe default. Anything that affects gameplay.
CLIENTYesNoClient-only mods and purely visual settings.
SERVERNoYesServer-side values edited only in the file. Rarely the right choice.

Config-level overrides

A config can override a few methods to change how it behaves as a whole.

OverrideDefaultWhat it changes
defaultPermLevel()2The operator level needed to edit the config on a server. Disabled in single player.
fileType()TOMLThe on-disk format. JSON5 is also supported.
saveType()OVERWRITEWhether a client writes server-pushed updates to its own local file. SEPARATE keeps them apart, so single player stays untouched.
update(version)no-opCalled when the file's version does not match @Version. Where you migrate old settings.

Opening the screen

Screens register themselves, so you do not have to. Players reach them through ModMenu on Fabric and Quilt, Catalogue on Forge and NeoForge, or the /configure command in game.

Need more depth on this topic? The wiki has a longer article. Read it on moddedmc.wiki.