Fix #146946: Breaking a node zone crashes on valid pointer assumption
The Node Wrangler addon has a _Reset Nodes_ operator that can remove the input node of a node zone. This crashes in reference set updates because the code expects valid input/output node pairs in each zone. The fix is two-fold: 1. Finding zones for the runtime now returns an empty result to ensure no invalid node pointers are being accessed. This should not happen in practice, all operators should make sure zone relationships are not broken. 2. The node wrangler addon is updated to ignore all zone types, including the newer repeat, closure, and for-each-element zones. The type filtering was outdated and now uses the `bl_idname` consistently. Pull Request: https://projects.blender.org/blender/blender/pulls/147028
This commit is contained in:
@@ -2261,13 +2261,28 @@ class NWResetNodes(bpy.types.Operator):
|
||||
and nw_check_selected(cls, context)
|
||||
and nw_check_active(cls, context))
|
||||
|
||||
@staticmethod
|
||||
def is_frame_node(node):
|
||||
return node.bl_idname == "NodeFrame"
|
||||
|
||||
group_node_types = {"CompositorNodeGroup", "GeometryNodeGroup", "ShaderNodeGroup"}
|
||||
# TODO All zone nodes are ignored here for now, because replacing one of the input/output pair breaks the zone.
|
||||
# It's possible to handle zones by using the `paired_output` function of an input node
|
||||
# and reconstruct the zone using the `pair_with_output` function.
|
||||
zone_node_types = {"GeometryNodeRepeatInput", "GeometryNodeRepeatOutput", "NodeClosureInput",
|
||||
"NodeClosureOutput", "GeometryNodeSimulationInput", "GeometryNodeSimulationOutput",
|
||||
"GeometryNodeForeachGeometryElementInput", "GeometryNodeForeachGeometryElementOutput"}
|
||||
node_ignore = group_node_types | zone_node_types | {"NodeFrame", "NodeReroute"}
|
||||
|
||||
@classmethod
|
||||
def ignore_node(cls, node):
|
||||
return node.bl_idname in cls.node_ignore
|
||||
|
||||
def execute(self, context):
|
||||
node_active = context.active_node
|
||||
node_selected = context.selected_nodes
|
||||
node_ignore = ["FRAME", "REROUTE", "GROUP", "SIMULATION_INPUT", "SIMULATION_OUTPUT"]
|
||||
|
||||
active_node_name = node_active.name if node_active.select else None
|
||||
valid_nodes = [n for n in node_selected if n.type not in node_ignore]
|
||||
valid_nodes = [n for n in node_selected if not self.ignore_node(n)]
|
||||
|
||||
# Create output lists
|
||||
selected_node_names = [n.name for n in node_selected]
|
||||
@@ -2275,18 +2290,18 @@ class NWResetNodes(bpy.types.Operator):
|
||||
|
||||
# Reset all valid children in a frame
|
||||
node_active_is_frame = False
|
||||
if len(node_selected) == 1 and node_active.type == "FRAME":
|
||||
if len(node_selected) == 1 and self.is_frame_node(node_active):
|
||||
node_tree = node_active.id_data
|
||||
children = [n for n in node_tree.nodes if n.parent == node_active]
|
||||
if children:
|
||||
valid_nodes = [n for n in children if n.type not in node_ignore]
|
||||
selected_node_names = [n.name for n in children if n.type not in node_ignore]
|
||||
valid_nodes = [n for n in children if not self.ignore_node(n)]
|
||||
selected_node_names = [n.name for n in children if not self.ignore_node(n)]
|
||||
node_active_is_frame = True
|
||||
|
||||
# Check if valid nodes in selection
|
||||
if not (len(valid_nodes) > 0):
|
||||
# Check for frames only
|
||||
frames_selected = [n for n in node_selected if n.type == "FRAME"]
|
||||
frames_selected = [n for n in node_selected if self.is_frame_node(n)]
|
||||
if (len(frames_selected) > 1 and len(frames_selected) == len(node_selected)):
|
||||
self.report({'ERROR'}, "Please select only 1 frame to reset")
|
||||
else:
|
||||
@@ -2306,7 +2321,6 @@ class NWResetNodes(bpy.types.Operator):
|
||||
|
||||
# Run through all valid nodes
|
||||
for node in valid_nodes:
|
||||
|
||||
parent = node.parent if node.parent else None
|
||||
node_loc = [node.location.x, node.location.y]
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ static Vector<std::unique_ptr<bNodeTreeZone>> find_zone_nodes(
|
||||
zone->index = zones.size();
|
||||
zone->output_node_id = node->identifier;
|
||||
r_zone_by_inout_node.add(node, zone.get());
|
||||
zones.append_and_get_index(std::move(zone));
|
||||
zones.append(std::move(zone));
|
||||
}
|
||||
for (const bNodeZoneType *zone_type : zone_types) {
|
||||
for (const bNode *input_node : tree.nodes_by_type(zone_type->input_idname)) {
|
||||
@@ -58,6 +58,13 @@ static Vector<std::unique_ptr<bNodeTreeZone>> find_zone_nodes(
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Avoid incomplete zones, all zones must have a valid input and output node. */
|
||||
for (const std::unique_ptr<bNodeTreeZone> &zone : zones) {
|
||||
if (!zone->input_node_id || !zone->output_node_id) {
|
||||
r_zone_by_inout_node.clear();
|
||||
return {};
|
||||
}
|
||||
}
|
||||
return zones;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user