Skip to content

Migration and Validation

When configuration structure changes, you can use "version + migration" to ensure smooth upgrade of old configurations and perform strict validation at runtime.

Version Declaration

Declare current version on configuration class:

java
@ConfigVersion(3)
public class MyConfig { /* ... */ }
  • If file doesn't contain config_version, treated as version 1
  • When library detects file version < class version, will migrate step by step (1→2→3...)

Custom Migration

Write static method for "old version number" in configuration class:

java
public static void migrateFromV1(java.util.Map<String, String> values) {
  // Example: rename key
  String oldKey = "general.oldSetting";
  String newKey = "general.newSetting";
  if (values.containsKey(oldKey) && !values.containsKey(newKey)) {
    values.put(newKey, values.remove(oldKey));
  }
}
  • Method signature must be: public static void migrateFromV{N}(Map<String,String> values)
  • Method should only depend on key-value pairs in values (all strings), responsible for transformation and validation
  • After migration, library writes config_version = <new version> and rewrites file (note: written as flattened key-value list)

Built-in example migrations (in library):

  • v1→v2: rendering.enableCullingrendering.entityCulling.enableCulling etc.
  • v2→v3: old.settingnew.setting

Validation

Performed during loading and reloading:

  1. Annotation validation
  • @Range: Out-of-range numeric values revert to default and log warning; also throws error in ConfigValidator
  • @Validation: Perform min/max length and regex validation on strings
  1. Custom overall validation
  • If config class contains public void validateConfig(), will be called for more complex logic

Practical Recommendations

  • Always increase @ConfigVersion when changing key names/hierarchy and provide corresponding migrateFromV{n} method
  • After migration, if wanting to restore pretty section/comment-friendly format, call save() once (e.g., from UI click "save")
  • Migration logic should be preferably "idempotent", multiple executions won't cause duplicate conversion