-
Notifications
You must be signed in to change notification settings - Fork 0
Versioning and Migrations
When a config class changes shape — a field is renamed, moved into a section, or split in two — older files still use the previous keys. Migrations transform those files before binding, preserving the values that remain meaningful in the new model.
Versioning is opt-in. A config that leaves version alone behaves exactly as before, and its file
gains nothing.
@Config(name = "mymod", version = 3)
public final class MyModConfig {
public HudSettings hud = new HudSettings();
public String profile = "default";
}A versioned file stores its revision under the reserved configVersion key, written first so it is
the first thing a reader sees:
{
"configVersion": 3,
"hud": { "scale": 2 },
"profile": "default"
}The key is reserved: a field that would claim it is rejected with RESERVED_VERSION_KEY when the
holder is built.
One step per version, written as a static method on the config class itself, next to the fields it upgrades. Each step moves the file forward by exactly one revision, and Lite Config runs the steps it needs in order.
@Config(name = "mymod", version = 3)
public final class MyModConfig {
public HudSettings hud = new HudSettings();
public String profile = "default";
@Migration(from = 1)
static void toVersion2(ConfigData data) {
data.rename("hudScale", "hud.scale");
}
@Migration(from = 2)
static void toVersion3(ConfigData data) {
data.set("profile", "default");
}
}A file at version 1 runs both steps; a file at version 2 runs only the second; a file already at version 3 runs none. The result is stamped with the declared version and saved.
A file that carries no configVersion key is treated as version 1, because that is what it was
before you started versioning.
When present, configVersion must be a positive whole number in the Java int range. null,
strings, booleans, objects, arrays, fractions, zero, negatives, and out-of-range numbers are
malformed config data. Only a completely absent key receives the version 1 compatibility default.
A step receives a ConfigData view of the parsed file. It works in dotted paths and plain Java
types, so a migration never depends on the file format or on the JSON library underneath.
@Migration(from = 1)
static void toVersion2(ConfigData data) {
data.rename("hudScale", "hud.scale")
.set("volume", data.integer("volume", 1) * 10)
.remove("legacyFlag");
}A step must be static, return void, and take a single ConfigData. A declaration that breaks
any of those rules, or a from value that two steps claim at once, is reported as
INVALID_MIGRATION when the holder is built.
The mutating calls return the same view, so a step usually reads as a short list of edits. Reads
come in two shapes: the Optional ones tell you whether the file had a value at all, and the ones
taking a fallback answer with a plain value, which is what a migration usually wants.
| Method | Purpose |
|---|---|
has(path) |
whether the path is present |
string, number, bool
|
read a value, empty when absent or of another kind |
string(path, fallback), integer, decimal, bool
|
read a value, falling back when the file has nothing usable |
set(path, value) |
write a value, creating the objects along the way |
remove(path) |
delete a value, doing nothing when it is absent |
rename(from, to) |
relocate a value, doing nothing when the source is absent |
Steps run against the raw file, before it is bound to your class, so a step can freely reference fields that no longer exist in Java.
| Situation | Error |
|---|---|
| The file's version is above the declared one | CONFIG_VERSION_TOO_NEW |
| No step is registered for a version the file has to cross | MISSING_CONFIG_MIGRATION |
| A step throws | MIGRATION_FAILED |
| A step is malformed, or two steps claim the same version | INVALID_MIGRATION |
CONFIG_VERSION_TOO_NEW means a player downgraded the mod, which is a runtime situation and follows
the read failure policy: FALLBACK moves the newer file to a .corrupt-... backup before saving
defaults, preserving all original data; STRICT throws and leaves the file in place. The other
three are mistakes in the mod itself and always propagate, whatever the policy. Every exception
thrown by a migration, including a LiteConfigException, is reported as MIGRATION_FAILED with
its root cause preserved.