-
Notifications
You must be signed in to change notification settings - Fork 0
Validation and Lifecycle Hooks
Implement ConfigExtension on the config class to participate in the load/save/validate
lifecycle:
@Config(name = "mymod")
public final class MyModConfig implements ConfigExtension {
public int hudScale = 2;
public String worldPreset = "default";
@Override
public void afterLoad() {
// Fix up values after loading — clamp ranges, derive computed fields, etc.
hudScale = Math.max(1, Math.min(8, hudScale));
}
@Override
public void beforeSave() {
// Normalize values before writing to disk, if needed.
// Most configs leave this empty.
}
@Override
public void validate(List<Violation> violations) {
// Report constraint violations. Must be side-effect free.
if (hudScale < 1 || hudScale > 8) {
violations.add(Violation.of("hud-scale.range", "hudScale must be between 1 and 8, was " + hudScale));
}
}
}Hook order and contract:
-
afterLoadruns after deserialization and before validation. It does not run for updates, so an update mutator must submit values that already satisfy validation. -
validateruns during holder construction, after each load, and before each update is published. It must be side-effect free: report invalid values rather than changing them. -
beforeSaveruns on a private copy immediately before serialization. Its changes reach the file without changing the published state.
Rules declared with @Range, @Pattern, and @Length are checked before
validate and produce violations of the same kind, so a simple bound needs no hook at all. See
Constraints and Metadata. Constraint annotations are checked against their field types when the
holder is built; invalid combinations fail with INVALID_CONSTRAINT rather than being silently
ignored during validation.
Handling validation failures:
Under FALLBACK update policy, violations come back on the UpdateResult:
UpdateResult result = holder.updateAndSave(config -> config.hudScale = 99);
if (!result.accepted()) {
result.violations().forEach(v -> LOGGER.warn("{}: {}", v.id(), v.message()));
}Under STRICT, the same violations ride on LiteConfigException.violations().
Validation on load: if the file on disk contains values that fail validate, the behavior
depends on @Config.readFailurePolicy. Under FALLBACK, the file is backed up and defaults are
restored. Holder creation writes those defaults immediately; a later load() keeps them in memory
until the next save. Under STRICT, the load throws.
These hooks belong to the config model. To react after a holder completes a load, update, or save, see Lifecycle Listeners.