Data Versioning (DataFixers)
As your plugin evolves, your data structures will inevitably change. You might rename configuration keys, flatten nested objects, or introduce entirely new mandatory fields. Instead of cluttering your codecs with complex if/else legacy support logic, AbyssalLib provides the DataFixerRegistry.
DataFixers operate directly on the raw DynamicOps tree before your codec attempts to decode it. By registering migrations from version 0 to version 1, version 1 to 2, etc., the system automatically upgrades any outdated data sequentially until it reaches your current schema.
1. Setting up the Registry
A DataFixerRegistry holds the migration logic for specific data types. You instantiate it with your "current" (target) schema version.
2. Deep Intersections via DataPath
Many built-in data fixers accept a path string. Under the hood, these strings are compiled into a structural DataPath, allowing you to seamlessly target deeply nested maps, array indices, or a combination of both without manually splitting strings or writing complex tree-navigation loops.
Supported syntax patterns include:
Standard Key:
"inventory"(Targets a property inside the current root object)Nested Nodes:
"inventory.weapon.damage"(Traverses down a tree of nested maps)List Indices:
"players[3]"(Targets the element at index 3 inside theplayerslist)Root List Index:
"[0]"(Targets the first element if the root configuration itself is an array)Complex Chains:
"players[3].inventory.weapon[0].damage"(Deep contextual traversal)
3. Built-in DataFixers
The DataFixer interface provides powerful static factory methods to handle data migrations cleanly.
Method | Description |
|---|---|
| Chains multiple fixers together to run sequentially on the same version step. |
| Renames a specific key within a map structure. |
| Deletes a key entirely from the map structure. |
| Injects a predefined primitive value if the key does not physically exist. |
| Moves explicitly defined sibling keys into a shared child nested map. |
| Elevates a deeply nested map key up to the parent map level. |
| Translates a compiled |
| Passes a specific map entry's value to a secondary fixer logic. |
| Executes a sub-fixer across all elements within a List/Array node. |
| Sorts keys within a map structure based on exact matches, wildcards ( |
| Redirects data handling based on a dynamic operations predicate. |
4. Registering Migrations
Register your schema changes into your registry. You specify the fromVersion that the fixer upgrades the data from. Use DataFixer.path() whenever you need to apply changes inside objects tucked away within lists or inner structures.
5. Applying the Versioned Modifier
Finally, wrap your standard codec using the .versioned() modifier, passing your Key and Registry.
How it works internally:
Decoding: The codec searches the raw data for a
data_versionkey. If the version is lower than the target version (or missing, defaulting to0), it runs the data through theDataFixerRegistryuntil it hits target version2. Then, it strips thedata_versionkey and hands the cleanly upgraded map to your underlyingRecordBuildercodec.Encoding: Your object encodes normally. Afterward, the
.versioned()wrapper automatically appends"data_version": 2to the output map so it is safely versioned for the future.