From 6e9e690d8af0243a1654d2c02ccfe1cb8aa92ab3 Mon Sep 17 00:00:00 2001 From: Adam Johnson Date: Wed, 4 Jul 2018 17:08:45 -0400 Subject: [PATCH] Ensure only valid responders can be graphed Responder commands are serialized, so it is not possible to execute a truly tree-shaped responder. Sigh. --- korman/nodes/node_core.py | 15 +++++++++++++++ korman/nodes/node_messages.py | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+) diff --git a/korman/nodes/node_core.py b/korman/nodes/node_core.py index c3dfa2a..ed439fe 100644 --- a/korman/nodes/node_core.py +++ b/korman/nodes/node_core.py @@ -196,6 +196,7 @@ class PlasmaNodeBase: i = 0 while i < len(sockets): socket = sockets[i] + node = socket.node options = defs.get(socket.alias, None) if options is None or socket.bl_idname != options["type"]: @@ -214,6 +215,20 @@ class PlasmaNodeBase: allowed_sockets = options.get("valid_link_sockets", None) allowed_nodes = options.get("valid_link_nodes", None) + # The socket may decide it doesn't want anyone linked to it. + can_link_attr = options.get("can_link", None) + if can_link_attr is not None: + can_link = getattr(node, can_link_attr) + socket.enabled = can_link + if not can_link: + for link in socket.links: + try: + self._tattle(socket, link, "(socket refused link)") + self.id_data.links.remove(link) + except RuntimeError: + # was already removed by someone else + pass + # Helpful default... If neither are set, require the link to be to the same socket type if allowed_nodes is None and allowed_sockets is None: allowed_sockets = frozenset((options["type"],)) diff --git a/korman/nodes/node_messages.py b/korman/nodes/node_messages.py index 2762c9c..3fff65c 100644 --- a/korman/nodes/node_messages.py +++ b/korman/nodes/node_messages.py @@ -48,17 +48,51 @@ class PlasmaMessageNode(PlasmaNodeBase): class PlasmaMessageWithCallbacksNode(PlasmaMessageNode): output_sockets = OrderedDict([ ("msgs", { + "can_link": "can_link_callback", "text": "Send On Completion", "type": "PlasmaMessageSocket", "valid_link_sockets": "PlasmaMessageSocket", }), ]) + @property + def can_link_callback(self): + """Determines if a callback message can be linked to this socket""" + + # Node Graphs enable us to draw lots of fancy logic, unfortunately, not + # everything that can potentially be represented in a node tree can be + # exported to URU in a way that will actually work. Responder commands can + # wait on other responder commands, but the way they are executed in Plasma is + # serialized. It's really a list of commands that are executed until a wait + # is encountered. At that time, Plasma waits and resumes running the list when + # the wait callback is received. + # So what does this mean??? + # It means that only one "branch" of message nodes can have waits. + def check_for_callbacks(parent_node, child_node): + for sibling_node in parent_node.find_outputs("msgs"): + if sibling_node == child_node: + continue + if getattr(sibling_node, "has_linked_callbacks", False): + return True + for grandparent_node in parent_node.find_inputs("sender"): + return check_for_callbacks(grandparent_node, parent_node) + return False + + for sender_node in self.find_inputs("sender"): + if check_for_callbacks(sender_node, self): + return False + return True + @property def has_callbacks(self): """This message has callbacks that can be waited on by a Responder""" return True + @property + def has_linked_callbacks(self): + return self.find_output("msgs") is not None + + class PlasmaAnimCmdMsgNode(idprops.IDPropMixin, PlasmaMessageWithCallbacksNode, bpy.types.Node): bl_category = "MSG" bl_idname = "PlasmaAnimCmdMsgNode"