APIUpdateConfig: How Ceres Handles Breaking Changes in Serialized Graphs
APIUpdateConfig is a ScriptableObject that acts as a migration layer, using three redirector tables to automatically map outdated node types, variables, and assembly strings to their new counterparts during graph deserialization.
The APIUpdateConfig class in the akikurisu/ceres repository solves the critical problem of backward compatibility when refactoring node classes, renaming namespaces, or restructuring assemblies. When you upgrade Ceres and encounter breaking changes in the public API, this configuration asset ensures existing serialized graphs load correctly without manual data migration or code changes.
The Three Redirector Tables in APIUpdateConfig
At the core of APIUpdateConfig are three specialized redirector arrays defined in Runtime/Core/Models/Graph/APIUpdateConfig.cs【^12-L73】. These tables store mapping rules that translate old serialized identities into current runtime types.
Node redirectors handle renamed or moved node classes. The array Redirector<SerializedManagedReferenceType<CeresNode>>[] nodeRedirectors maps obsolete node type definitions to their replacements using full assembly-qualified names.
Variable redirectors manage changes to shared variable implementations. The Redirector<SerializedManagedReferenceType<SharedVariable>>[] variableRedirectors array performs the same function for SharedVariable subclasses, ensuring blackboard variables survive refactoring.
Assembly and namespace redirectors solve string-level changes. The Redirector<string>[] entries rewrite assembly or namespace substrings embedded inside SerializedType strings, catching references that persist as text in serialized fields.
Activation via ConfigAutoScope
Ceres activates the migration layer only during graph construction to minimize runtime overhead. The APIUpdateConfig.AutoScope() method creates a temporary ConfigAutoScope that sets the static _activeConfig field to the selected asset【^73-L94】.
When a graph builds, CeresGraph wraps deserialization in a using (APIUpdateConfig.AutoScope()) block【^10-L17】. This scope automatically wires the SerializedTypeRedirector.RedirectSerializedType event if auto-redirect is enabled, then disposes after construction completes, resetting _activeConfig to null.
Redirect Logic During Deserialization
The actual type resolution occurs inside CeresGraphData methods, which consult APIUpdateConfig.Current only when a config is active.
Node restoration follows this path: RestoreNode calls APIUpdateConfig.Current.RedirectNodeType, which iterates nodeRedirectors to find a matching source type and returns the new System.Type (or null if no match exists)【^72-L78】【^81-L86】.
Variable restoration mirrors the node pattern. RestoreVariable invokes RedirectVariableType to check variableRedirectors and substitute outdated variable classes before instantiation【^102-L108】.
Serialized type strings undergo substring rewriting. RedirectSerializedType uses the string redirectors to replace assembly or namespace fragments inside type identity strings【^118-L138】. CeresGraphData.ResolveSerializedType triggers this rewrite when the enableAutoRedirectSerializedType flag is active【^56-L64】.
Safety Checks and Validation
All redirection calls are guarded by Assert.IsTrue((bool)APIUpdateConfig.Current) to ensure a valid configuration is active during deserialization【^24-L27】. This prevents silent null-reference failures if a developer forgets to assign the config asset, failing fast only in development builds while allowing production builds to skip the overhead entirely when no migration is needed.
Workflow for Handling a Breaking Change
When a Ceres upgrade introduces incompatible type changes, follow this migration workflow:
- Create an APIUpdateConfig asset via Assets → Create → Ceres → API Update Config (or use the editor script below).
- Populate redirect entries in the Unity Inspector, mapping old assembly-qualified names to new ones.
- Enable auto-redirect for string-based types by checking
enableAutoRedirectSerializedTypeif namespace or assembly strings changed. - Commit the asset to version control so team members and CI builds automatically apply the same migration rules.
Code Examples
Creating an APIUpdateConfig Asset via Editor Script
Place this in an Editor folder to add a menu item for rapid config creation:
using UnityEditor;
using Ceres.Graph;
public static class APIUpdateConfigCreator
{
[MenuItem("Assets/Create/Ceres/API Update Config")]
public static void Create()
{
var config = ScriptableObject.CreateInstance<APIUpdateConfig>();
AssetDatabase.CreateAsset(config, "Assets/APIUpdateConfig.asset");
AssetDatabase.SaveAssets();
EditorUtility.FocusProjectWindow();
Selection.activeObject = config;
}
}
Configuring Node Redirectors Programmatically
This example demonstrates the structure for remapping an obsolete node class to its replacement:
var config = ScriptableObject.CreateInstance<APIUpdateConfig>();
config.nodeRedirectors = new[]
{
new APIUpdateConfig.Redirector<APIUpdateConfig.SerializedManagedReferenceType<CeresNode>>
{
source = new APIUpdateConfig.SerializedManagedReferenceType<CeresNode>(
new ManagedReferenceType("OldNamespace", "OldNodeName", "OldAssembly")),
target = new APIUpdateConfig.SerializedManagedReferenceType<CeresNode>(
new ManagedReferenceType("NewNamespace", "NewNodeName", "NewAssembly"))
}
};
config.enableAutoRedirectSerializedType = true;
Loading a Graph with Automatic Migration
No manual intervention is required when loading graphs; the scope is applied internally:
// AutoScope is invoked automatically inside CeresGraph constructor
var graph = new CeresGraph(savedGraphData);
Summary
- APIUpdateConfig is a ScriptableObject that stores migration rules for three categories: nodes, variables, and assembly/namespace strings.
- The ConfigAutoScope pattern ensures redirect logic is active only during deserialization, minimizing runtime performance impact.
- RedirectNodeType, RedirectVariableType, and RedirectSerializedType perform the actual type substitution in
CeresGraphData. - Safety assertions validate that a config is active before any redirection occurs, preventing silent failures.
- Breaking changes are resolved by creating and committing an APIUpdateConfig asset, allowing existing graph files to load seamlessly after refactoring.
Frequently Asked Questions
What happens if no APIUpdateConfig is assigned when loading an old graph?
If APIUpdateConfig.Current is null, the deserialization methods in CeresGraphData skip all redirection logic and attempt to instantiate types using their original serialized identities. This results in standard Unity serialization errors (missing scripts or null references) if the types no longer exist, but prevents silent data corruption by failing explicitly.
Can I use multiple APIUpdateConfig assets for different graph collections?
Yes, you can create multiple assets, but only one can be active per deserialization scope. The AutoScope() method accepts an optional config parameter, allowing you to specify which migration rules apply when manually constructing specific graphs. For most projects, a single global config committed to version control is sufficient.
Does APIUpdateConfig affect runtime performance after graph initialization?
No. Because ConfigAutoScope wraps only the construction phase and resets _activeConfig immediately after, the redirection lookup tables are not consulted during graph execution. The migration overhead is limited to the initial new CeresGraph(data) call, making this approach safe for production builds with frequent graph instantiation.
How do I migrate types stored as strings in custom serialized fields?
Enable enableAutoRedirectSerializedType on your APIUpdateConfig asset and add entries to the string redirector table. The RedirectSerializedType method rewrites assembly and namespace substrings in SerializedType strings during CeresGraphData.ResolveSerializedType, catching edge cases where types are stored as text rather than managed references.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →