From bce14549ac143169c8ab12ae5719d11a6b0bd8fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Moritz=20Br=C3=BCckner?= Date: Mon, 26 Jul 2021 00:02:54 +0200 Subject: [PATCH] Fix LN live patch when using multiple instances of the same logic tree --- Sources/armory/logicnode/LogicTree.hx | 5 +- Sources/armory/trait/internal/LivePatch.hx | 204 +++++++++++---------- blender/arm/make_logic.py | 21 ++- 3 files changed, 127 insertions(+), 103 deletions(-) diff --git a/Sources/armory/logicnode/LogicTree.hx b/Sources/armory/logicnode/LogicTree.hx index 22daf13a..ed06b08e 100644 --- a/Sources/armory/logicnode/LogicTree.hx +++ b/Sources/armory/logicnode/LogicTree.hx @@ -3,7 +3,10 @@ package armory.logicnode; class LogicTree extends iron.Trait { #if arm_patch - public static var nodeTrees = new Map(); + /** + Stores all trait instances of the tree via its name. + **/ + public static var nodeTrees = new Map>(); /** [node name => logic node] for later node replacement for live patching. diff --git a/Sources/armory/trait/internal/LivePatch.hx b/Sources/armory/trait/internal/LivePatch.hx index d4548957..743f66be 100644 --- a/Sources/armory/trait/internal/LivePatch.hx +++ b/Sources/armory/trait/internal/LivePatch.hx @@ -33,147 +33,161 @@ class LivePatch extends iron.Trait { } public static function patchCreateNodeLink(treeName: String, fromNodeName: String, toNodeName: String, fromIndex: Int, toIndex: Int) { - var tree = LogicTree.nodeTrees[treeName]; - if (tree == null) return; + if (!LogicTree.nodeTrees.exists(treeName)) return; + var trees = LogicTree.nodeTrees[treeName]; - var fromNode = tree.nodes[fromNodeName]; - var toNode = tree.nodes[toNodeName]; - if (fromNode == null || toNode == null) return; + for (tree in trees) { + var fromNode = tree.nodes[fromNodeName]; + var toNode = tree.nodes[toNodeName]; + if (fromNode == null || toNode == null) return; - LogicNode.addLink(fromNode, toNode, fromIndex, toIndex); + LogicNode.addLink(fromNode, toNode, fromIndex, toIndex); + } } public static function patchSetNodeLinks(treeName: String, nodeName: String, inputDatas: Array, outputDatas: Array>) { - var tree = LogicTree.nodeTrees[treeName]; - if (tree == null) return; + if (!LogicTree.nodeTrees.exists(treeName)) return; + var trees = LogicTree.nodeTrees[treeName]; - var node = tree.nodes[nodeName]; - if (node == null) return; + for (tree in trees) { + var node = tree.nodes[nodeName]; + if (node == null) return; - node.clearInputs(); - node.clearOutputs(); + node.clearInputs(); + node.clearOutputs(); - for (inputData in inputDatas) { - var fromNode: LogicNode; - var fromIndex: Int; + for (inputData in inputDatas) { + var fromNode: LogicNode; + var fromIndex: Int; - if (inputData.isLinked) { - fromNode = tree.nodes[inputData.fromNode]; - if (fromNode == null) continue; - fromIndex = inputData.fromIndex; - } - else { - fromNode = LogicNode.createSocketDefaultNode(node.tree, inputData.socketType, inputData.socketValue); - fromIndex = 0; - } - - LogicNode.addLink(fromNode, node, fromIndex, inputData.toIndex); - } - - for (outputData in outputDatas) { - for (linkData in outputData) { - var toNode: LogicNode; - var toIndex: Int; - - if (linkData.isLinked) { - toNode = tree.nodes[linkData.toNode]; - if (toNode == null) continue; - toIndex = linkData.toIndex; + if (inputData.isLinked) { + fromNode = tree.nodes[inputData.fromNode]; + if (fromNode == null) continue; + fromIndex = inputData.fromIndex; } else { - toNode = LogicNode.createSocketDefaultNode(node.tree, linkData.socketType, linkData.socketValue); - toIndex = 0; + fromNode = LogicNode.createSocketDefaultNode(node.tree, inputData.socketType, inputData.socketValue); + fromIndex = 0; } - LogicNode.addLink(node, toNode, linkData.fromIndex, toIndex); + LogicNode.addLink(fromNode, node, fromIndex, inputData.toIndex); + } + + for (outputData in outputDatas) { + for (linkData in outputData) { + var toNode: LogicNode; + var toIndex: Int; + + if (linkData.isLinked) { + toNode = tree.nodes[linkData.toNode]; + if (toNode == null) continue; + toIndex = linkData.toIndex; + } + else { + toNode = LogicNode.createSocketDefaultNode(node.tree, linkData.socketType, linkData.socketValue); + toIndex = 0; + } + + LogicNode.addLink(node, toNode, linkData.fromIndex, toIndex); + } } } } public static function patchUpdateNodeProp(treeName: String, nodeName: String, propName: String, value: Dynamic) { - var tree = LogicTree.nodeTrees[treeName]; - if (tree == null) return; + if (!LogicTree.nodeTrees.exists(treeName)) return; + var trees = LogicTree.nodeTrees[treeName]; - var node = tree.nodes[nodeName]; - if (node == null) return; + for (tree in trees) { + var node = tree.nodes[nodeName]; + if (node == null) return; - Reflect.setField(node, propName, value); + Reflect.setField(node, propName, value); + } } public static function patchUpdateNodeInputVal(treeName: String, nodeName: String, socketIndex: Int, value: Dynamic) { - var tree = LogicTree.nodeTrees[treeName]; - if (tree == null) return; + if (!LogicTree.nodeTrees.exists(treeName)) return; + var trees = LogicTree.nodeTrees[treeName]; - var node = tree.nodes[nodeName]; - if (node == null) return; + for (tree in trees) { + var node = tree.nodes[nodeName]; + if (node == null) return; - node.inputs[socketIndex].set(value); + node.inputs[socketIndex].set(value); + } } public static function patchNodeDelete(treeName: String, nodeName: String) { - var tree = LogicTree.nodeTrees[treeName]; - if (tree == null) return; + if (!LogicTree.nodeTrees.exists(treeName)) return; + var trees = LogicTree.nodeTrees[treeName]; - var node = tree.nodes[nodeName]; - if (node == null) return; + for (tree in trees) { + var node = tree.nodes[nodeName]; + if (node == null) return; - node.clearOutputs(); - node.clearInputs(); - tree.nodes.remove(nodeName); + node.clearOutputs(); + node.clearInputs(); + tree.nodes.remove(nodeName); + } } public static function patchNodeCreate(treeName: String, nodeName: String, nodeType: String, propDatas: Array>, inputDatas: Array>, outputDatas: Array>) { - var tree = LogicTree.nodeTrees[treeName]; - if (tree == null) return; + if (!LogicTree.nodeTrees.exists(treeName)) return; + var trees = LogicTree.nodeTrees[treeName]; - // No further constructor parameters required here, all variable nodes - // use optional further parameters and all values are set later in this - // function. - var newNode: LogicNode = Type.createInstance(Type.resolveClass(nodeType), [tree]); - newNode.name = nodeName; - tree.nodes[nodeName] = newNode; + for (tree in trees) { + // No further constructor parameters required here, all variable nodes + // use optional further parameters and all values are set later in this + // function. + var newNode: LogicNode = Type.createInstance(Type.resolveClass(nodeType), [tree]); + newNode.name = nodeName; + tree.nodes[nodeName] = newNode; - for (propData in propDatas) { - Reflect.setField(newNode, propData[0], propData[1]); - } + for (propData in propDatas) { + Reflect.setField(newNode, propData[0], propData[1]); + } - var i = 0; - for (inputData in inputDatas) { - LogicNode.addLink(LogicNode.createSocketDefaultNode(newNode.tree, inputData[0], inputData[1]), newNode, 0, i++); - } + var i = 0; + for (inputData in inputDatas) { + LogicNode.addLink(LogicNode.createSocketDefaultNode(newNode.tree, inputData[0], inputData[1]), newNode, 0, i++); + } - i = 0; - for (outputData in outputDatas) { - LogicNode.addLink(newNode, LogicNode.createSocketDefaultNode(newNode.tree, outputData[0], outputData[1]), i++, 0); + i = 0; + for (outputData in outputDatas) { + LogicNode.addLink(newNode, LogicNode.createSocketDefaultNode(newNode.tree, outputData[0], outputData[1]), i++, 0); + } } } public static function patchNodeCopy(treeName: String, nodeName: String, newNodeName: String, copyProps: Array, inputDatas: Array>, outputDatas: Array>) { - var tree = LogicTree.nodeTrees[treeName]; - if (tree == null) return; + if (!LogicTree.nodeTrees.exists(treeName)) return; + var trees = LogicTree.nodeTrees[treeName]; - var node = tree.nodes[nodeName]; - if (node == null) return; + for (tree in trees) { + var node = tree.nodes[nodeName]; + if (node == null) return; - // No further constructor parameters required here, all variable nodes - // use optional further parameters and all values are set later in this - // function. - var newNode: LogicNode = Type.createInstance(Type.getClass(node), [tree]); - newNode.name = newNodeName; - tree.nodes[newNodeName] = newNode; + // No further constructor parameters required here, all variable nodes + // use optional further parameters and all values are set later in this + // function. + var newNode: LogicNode = Type.createInstance(Type.getClass(node), [tree]); + newNode.name = newNodeName; + tree.nodes[newNodeName] = newNode; - for (propName in copyProps) { - Reflect.setField(newNode, propName, Reflect.field(node, propName)); - } + for (propName in copyProps) { + Reflect.setField(newNode, propName, Reflect.field(node, propName)); + } - var i = 0; - for (inputData in inputDatas) { - LogicNode.addLink(LogicNode.createSocketDefaultNode(newNode.tree, inputData[0], inputData[1]), newNode, 0, i++); - } + var i = 0; + for (inputData in inputDatas) { + LogicNode.addLink(LogicNode.createSocketDefaultNode(newNode.tree, inputData[0], inputData[1]), newNode, 0, i++); + } - i = 0; - for (outputData in outputDatas) { - LogicNode.addLink(newNode, LogicNode.createSocketDefaultNode(newNode.tree, outputData[0], outputData[1]), i++, 0); + i = 0; + for (outputData in outputDatas) { + LogicNode.addLink(newNode, LogicNode.createSocketDefaultNode(newNode.tree, outputData[0], outputData[1]), i++, 0); + } } } diff --git a/blender/arm/make_logic.py b/blender/arm/make_logic.py index 0e74d043..5ecf42a1 100755 --- a/blender/arm/make_logic.py +++ b/blender/arm/make_logic.py @@ -79,7 +79,14 @@ def build_node_tree(node_group: 'arm.nodes_logic.ArmLogicTree'): f.write('\t\tthis.functionNodes = new Map();\n') f.write('\t\tthis.functionOutputNodes = new Map();\n') if arm.utils.is_livepatch_enabled(): - f.write(f'\t\tarmory.logicnode.LogicTree.nodeTrees["{group_name}"] = this;\n') + # Store a reference to this trait instance in Logictree.nodeTrees + f.write('\t\tvar nodeTrees = armory.logicnode.LogicTree.nodeTrees;\n') + f.write(f'\t\tif (nodeTrees.exists("{group_name}")) ' + '{\n') + f.write(f'\t\t\tnodeTrees["{group_name}"].push(this);\n') + f.write('\t\t} else {\n') + f.write(f'\t\t\tnodeTrees["{group_name}"] = cast [this];\n') + f.write('\t\t}\n') + f.write('\t\tnotifyOnRemove(() -> { nodeTrees.remove("' + group_name + '"); });\n') f.write('\t\tnotifyOnAdd(add);\n') f.write('\t}\n\n') f.write('\toverride public function add() {\n') @@ -217,9 +224,9 @@ def build_node(node: bpy.types.Node, f: TextIO) -> Optional[str]: f.write(f'\t\t{"var __link = " if use_live_patch else ""}armory.logicnode.LogicNode.addLink({inp_name}, {name}, {inp_from}, {idx});\n') if use_live_patch: to_type = inp.arm_socket_type - f.write(f'\t\t__link.fromType = "{from_type}";') - f.write(f'\t\t__link.toType = "{to_type}";') - f.write(f'\t\t__link.toValue = {arm.node_utils.haxe_format_socket_val(inp.get_default_value())};') + f.write(f'\t\t__link.fromType = "{from_type}";\n') + f.write(f'\t\t__link.toType = "{to_type}";\n') + f.write(f'\t\t__link.toValue = {arm.node_utils.haxe_format_socket_val(inp.get_default_value())};\n') # Create outputs for idx, out in enumerate(node.outputs): @@ -229,9 +236,9 @@ def build_node(node: bpy.types.Node, f: TextIO) -> Optional[str]: f.write(f'\t\t{"var __link = " if use_live_patch else ""}armory.logicnode.LogicNode.addLink({name}, {build_default_node(out)}, {idx}, 0);\n') if use_live_patch: out_type = out.arm_socket_type - f.write(f'\t\t__link.fromType = "{out_type}";') - f.write(f'\t\t__link.toType = "{out_type}";') - f.write(f'\t\t__link.toValue = {arm.node_utils.haxe_format_socket_val(out.get_default_value())};') + f.write(f'\t\t__link.fromType = "{out_type}";\n') + f.write(f'\t\t__link.toType = "{out_type}";\n') + f.write(f'\t\t__link.toValue = {arm.node_utils.haxe_format_socket_val(out.get_default_value())};\n') return name