//============= Copyright (c) Ludic GmbH, All rights reserved. ============== // // Purpose: Part of the My Behaviour Tree Code // //============================================================================= using System; using System.Collections; using System.Collections.Generic; using System.Linq; using UnityEngine; namespace MyBT { public class MethodBinder { /// /// debug most things /// public static bool debug = false; /// /// debug the isbound function /// public static bool debugIsBound = false; /// /// debug runtree node binding /// public static bool debugRunTreeBinding = false; /// /// debug action node binding /// public static bool debugActionBinding = false; public static int maximumFunctionDepth = 12; #region check if generated public static bool VerifyBinding (List treeRootNodes) { return IsAllBound(treeRootNodes); } private static bool IsAllBound(List treeRootNodes) { if (treeRootNodes != null) { bool allBound = true; foreach (NodeList treeRoots in treeRootNodes) { if (treeRoots != null) { foreach (Node treeNode in treeRoots) { if (treeNode != null) { foreach (Node node in treeNode.getAllNodesRecursively(false).OrderBy(o => o.lineNumber).ToList()) { bool bound = IsBound(node); allBound &= bound; if (debugIsBound) { if (bound) { Debug.Log($"MethodBinder.IsBound: {node.GetType().Name} {node.codeLine} {node.codeLine} bound: {bound}"); } else { Debug.LogWarning($"MethodBinder.IsBound: {node.GetType().Name} {node.codeLine} {node.codeLine} bound: {bound}"); } } } } } } } return allBound; } return false; } private static bool IsBound(Node currentNode) { if (currentNode != null) { // based on current parent node: switch (Node.NodeTypeDict[currentNode.GetType()]) { case Node.TaskNodeType.RunTreeNode: RunTreeNode runTreeNode = currentNode as RunTreeNode; // if this runTreeNode's function is not bound (called), it will also not be bound, ignore in this case bool bound = runTreeNode.runThisNode != null; if (debugIsBound) { if (bound) { Debug.Log($"MethodBinder.IsBound: {runTreeNode} bound={bound}"); } else { Debug.LogWarning($"MethodBinder.IsBound: {runTreeNode} bound={bound}"); } } return bound; case Node.TaskNodeType.ActionNode: ActionNode actionNode = currentNode as ActionNode; if (debugIsBound) { if (actionNode.isBound) { Debug.Log($"MethodBinder.IsBound: {actionNode} action:{actionNode.action} bound={actionNode.isBound}"); } else { Debug.LogWarning($"MethodBinder.IsBound: {actionNode} action:{actionNode.action} bound={actionNode.isBound}"); } } return (actionNode.isBound); case Node.TaskNodeType.TaskNode: case Node.TaskNodeType.TreeNode: case Node.TaskNodeType.DecoratorNode: case Node.TaskNodeType.CompositeNode: case Node.TaskNodeType.CommentNode: // no binding of those nodes implemented or required break; } } return true; } #endregion #region binding of the methods to the nodes public static bool Bind(ref List treeRootNodes, TreeNode rootNode, DictionaryOfStringAndDictionaryOfStringAndMethodImplementation tasks) { bool success = true; ResetAll(ref treeRootNodes); if (treeRootNodes != null) { // bind actions if (debugActionBinding) Debug.Log($"MethodBinder.Bind: bind actions"); for (int nId=0; nId o.lineNumber).ToList()) { if (node.GetType() == typeof(ActionNode)) { if (debugActionBinding) Debug.Log($"MethodBinder.Bind: bind action {node}"); bool bindSuccess = BindActionNode(node as ActionNode, tasks); if (!bindSuccess) { Debug.LogError("binding failed"); } success &= bindSuccess; } //foreach (Node node in rootNode.getAllNodesRecursively(true).OrderBy(o => o.lineNumber).ToList()) { if (node.GetType() == typeof(RunTreeNode)) { RunTreeNode runTreeNode = node as RunTreeNode; if (debugRunTreeBinding) Debug.Log($"MethodBinder.Bind: runTreeNode: {runTreeNode} has bound: {runTreeNode.runThisNode}"); if (runTreeNode.runThisNode == null) { bool bindSuccess = BindRunTreeNode(runTreeNode, ref treeRootNodes); if (bindSuccess) { if (debugRunTreeBinding) Debug.Log($"MethodBinder.Bind: new binding {node} to {runTreeNode.runThisNode}"); } else { Debug.LogError(runTreeNode.NodeLogger("BindRunTree", $"binding failed")); } success &= bindSuccess; } else { if (debugRunTreeBinding) Debug.Log($"MethodBinder.Bind: already bound {runTreeNode} to {runTreeNode.runThisNode}"); } } } } } else { Debug.LogError($"MethodBinder.Bind: treeRootNodes is null {treeNodes}"); success &= false; } } } else { Debug.LogError("MethodBinder.Bind: treeRootNodes is null"); success &= false; } return IsAllBound(treeRootNodes) && success; } public static bool BindRunTreeNode(RunTreeNode runTreeNode, ref List treeRootNodes, bool allowDuplication = true) { for (int tnId=0; tnId < treeRootNodes.Count; tnId++) { NodeList nodes = treeRootNodes[tnId]; for (int nId = 0; nId < nodes.Count; nId++) { TreeNode treeNode = nodes[nId] as TreeNode; if (runTreeNode.runTreeNodeName == treeNode.treeNodeName) { runTreeNode.runThisNode = treeNode; return true; } } } return false; } public static bool BindActionNode (ActionNode actionNode, DictionaryOfStringAndDictionaryOfStringAndMethodImplementation tasks) { bool success = true; if (actionNode != null) { string functionName = actionNode.functionName; TaskParameterGroup taskParameterGroup = actionNode.taskParameterGroup; DictionaryOfStringAndDictionaryOfStringAndMethodImplementation taskParameterImplementationDict = tasks; if (taskParameterImplementationDict.ContainsKey(functionName)) { MethodImplementation implementation = MethodBinder.FindMatchingImplementation(taskParameterImplementationDict[functionName], taskParameterGroup); if (implementation != null) { Action action; success &= implementation.GetAction(taskParameterGroup, out action); if (debugActionBinding) { Debug.Log($"MethodBinder.NodeBind: binding {actionNode.functionName} to {implementation} with {taskParameterGroup}"); for (int i=0; i< taskParameterGroup.Count; i++) { Debug.Log($"MethodBinder.NodeBind: parameter {i}: {taskParameterGroup[i].valType} {taskParameterGroup[i].valValue}"); } } // Debug.Log("TaskScriptParser.RunScript: accepted action matching " + functionName + " " + implementation); actionNode.action = action; } else { Debug.LogError($"MethodBinder.NodeBind: no matching implementation found for {functionName} {taskParameterGroup}"); success &= false; } } else { string logMsg = $"TaskScriptParser.RunScript: No matching function found {functionName} ({taskParameterGroup})\navailable are:"; foreach (KeyValuePair kvp in taskParameterImplementationDict) { string arguments = string.Join(" | ", kvp.Value.Keys); logMsg += $", {kvp.Key} ({arguments})"; } Debug.LogError(logMsg); success &= false; } } else { success &= false; Debug.LogError("MethodBinder.NodeBind: actionNode undefined!"); } return success; } public static MethodImplementation FindMatchingImplementation(DictionaryOfStringAndMethodImplementation taskImplementations, TaskParameterGroup taskParameterGroup) { string taskParmStr = taskParameterGroup.ToString(); // if the parameters match exactly if (taskImplementations.ContainsKey(taskParmStr)) { return taskImplementations[taskParmStr]; } // more complex implementation of finding the right implementation for the given parameters else { // use this for getting the right one // https://stackoverflow.com/questions/3631547/select-right-generic-method-with-reflection // maybe just omit this and let it do without checking myself, but having the automatic system do it...? //Debug.LogWarning("TaskScriptParser.FindMatchingImplementation: no matching implementation found for " + taskParameterGroup); foreach (KeyValuePair kvp in taskImplementations) { if (kvp.Value.parameters.CheckCompatiblity(taskParameterGroup)) { if (debug) Debug.Log($"TaskScriptParser.FindMatchingImplementation: possible implementations {kvp.Key} accepted {kvp.Value} : {taskParameterGroup}"); return kvp.Value; } else { if (debug) Debug.Log($"TaskScriptParser.FindMatchingImplementation: possible implementations {kvp.Key} ignored : {kvp.Value} : {taskParameterGroup}"); } } } // get some implementation, all will have the same name string functionName = taskImplementations[taskImplementations.Keys.First()].fullName; Debug.LogError("MethodBinder.FindMatchingImplementation: no matching implementation found for " + functionName + " : " + taskParmStr); return null; } #endregion #region reset the bindings /// /// Clear All associated data from all Nodes /// /// public static void ResetAll(ref List treeRootNodes) { if (debug) Debug.Log("MethodBinder.ResetAll"); if (treeRootNodes != null) { foreach (NodeList treeRoots in treeRootNodes) { foreach (Node treeNode in treeRoots) { if (treeNode != null) { foreach (Node node in treeNode.getAllNodesRecursively(false).OrderBy(o => o.lineNumber).ToList()) { NodeClear(node); } } } } } } private static void NodeClear(Node currentNode) { if (currentNode != null) { // based on current parent node: switch (Node.NodeTypeDict[currentNode.GetType()]) { case Node.TaskNodeType.RunTreeNode: RunTreeNode runTreeNode = currentNode as RunTreeNode; runTreeNode.runThisNode = null; break; case Node.TaskNodeType.TreeNode: TreeNode treeNode = (currentNode as TreeNode); break; case Node.TaskNodeType.ActionNode: ActionNode actionNode = currentNode as ActionNode; actionNode.action = null; break; case Node.TaskNodeType.TaskNode: case Node.TaskNodeType.DecoratorNode: case Node.TaskNodeType.CompositeNode: case Node.TaskNodeType.CommentNode: // no binding of those nodes implemented or required break; } } } #endregion } }