307 lines
15 KiB
C#
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
|
|
|
|
}
|
|
} |