UP-Viagg-io/Viagg-io/Assets/Packages/MyBT/BT/Compiler/MethodBinder/MethodBinder.cs

307 lines
15 KiB
C#

//============= 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 {
/// <summary>
/// debug most things
/// </summary>
public static bool debug = false;
/// <summary>
/// debug the isbound function
/// </summary>
public static bool debugIsBound = false;
/// <summary>
/// debug runtree node binding
/// </summary>
public static bool debugRunTreeBinding = false;
/// <summary>
/// debug action node binding
/// </summary>
public static bool debugActionBinding = false;
public static int maximumFunctionDepth = 12;
#region check if generated
public static bool VerifyBinding (List<NodeList> treeRootNodes) {
return IsAllBound(treeRootNodes);
}
private static bool IsAllBound(List<NodeList> 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<NodeList> 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<treeRootNodes.Count; nId++) {
NodeList treeNodes = treeRootNodes[nId];
if (treeNodes != null) {
foreach (Node treeNode in treeNodes) {
foreach (Node node in treeNode.getAllNodesRecursively(false).OrderBy(o => 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<NodeList> 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<string, DictionaryOfStringAndMethodImplementation> 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<string, MethodImplementation> 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
/// <summary>
/// Clear All associated data from all Nodes
/// </summary>
/// <param name="treeRootNodes"></param>
public static void ResetAll(ref List<NodeList> 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
}
}