Nodes upgrade approaches: discussion
Created by: portnov
Problem statement
Sometimes we have to change some node in incompatible way. Examples of such changes are:
- Changing type of property
- Changing type of socket
- Removing a property
- Adding input or output not to the end of list
- etc (it would be nice to have full list of such changes written somewhere, for example right here)
We cant "just change" node code in such situations, because in existing blend files nodes are already set up in the old way, so the new code will not know how to deal with old node.
Current solution
- Create new node with new bl_idname
- Move old node to old_nodes
Drawbacks of current approach
- Users have to manually remove old nodes from their setups and create new ones, if they do not want to see ugly red "Deprecated" frames
- We have to maintain several versions of the same code
- In some situations, we have several MkN non-deprecated versions of the same node at the same time.
Suggested approach
- Introduce a new IntProperty in base node class,
node_version
, with default value of 0. This property is not displayed anywhere in UI, but it will be stored in blend files as all properties are. This property will mean the version of node code, which was used when this node was created; so this is the version of stored node settings. - Introduce a new method in base node class,
get_current_version(self)
. Return value of this method will mean current version of node code. Default implementation just always returns 0. - In base implementation of
node.init()
(not even sv_init, because this one is overriden everywhere) doself.node_version = get_self.get_current_version()
. - Introduce a new method in base node class,
upgrade_version(self, old_version, new_version)
. This method should re-setup node properties and sockets, considering that node was created withold_version
of code, and current version of code isnew_version
. - At event of scene loading, loop through all nodes that have
node_version != get_current_version()
, and mark them as "needing upgrade" somehow (by using node.color or a frame). - For actually upgrading node settings, I see two possible approaches:
- Call
node.upgrade_version()
automatically at scene loading; This has obvious advantage, which is that user does not have to do anything to upgrade the whole setup. - Or have a special bpy.types.Operator, that calls
upgrade_version
for selected node, and a button for it somewhere, so that user would upgrade nodes manually. This has another advantage: Operator can interact with user somehow, for example ask for initial value of newly introduced property. But, in this approach, we must have in mind, that there can exist non-upgraded nodes, so inprocess()
we will have to add checks likeif self.node_version == 0: don't try to use new properties
.
- Call
- Similar logic should be triggered after importing nodetree from json.
- Anyway, after
node.upgrade_version()
is called, we should donode.node_version = node.get_current_version()
. - When we want to introduce an incompatible change, we will have to:
- override
get_current_version()
or increase version number in already existing override (just writereturn 7
instead ofreturn 6
there). - override
upgrade_version()
or write a new branch in existing one (if old_version == 6 and new_version == 7: self.outputs.new(), self.new_property = default_value and so on)
- override
Obviously, the new approach is to be used only for new situations when we would change nodes in incompatible way; already existing old_nodes will live as they are.
@zeffii @nortikin @DolphinDream any thoughts?