UP-Viagg-io/Viagg-io/Assets/Packages/MyBT/BT/Editor/BtCodeInspector.cs

507 lines
29 KiB
C#

//============= Copyright (c) Ludic GmbH, All rights reserved. ==============
//
// Purpose: Part of the My Behaviour Tree Code
//
//=============================================================================
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System;
#if UNITY_EDITOR
using UnityEditor;
using UnityEditor.SceneManagement;
#endif
using UnityEngine;
namespace MyBT {
public class CodeInspector : ScriptableObject {
#if UNITY_EDITOR
#region type definitions
[System.Serializable] public class DictionaryOfIntAndNodeList : MyBT.SerializableDictionary<int, NodeList> { }
#endregion
[SerializeField]
private List<DictionaryOfIntAndNodeList> openNodeLines;
private List<DictionaryOfIntAndNodeList> closeNodeLines;
public SerializedProperty openNodeLinesAsSerializedProperty {
get {
SerializedObject sObj = new SerializedObject(this);
return sObj.FindProperty("openNodeLines");
}
}
[SerializeField]
private TaskController taskController;
public void Init(TaskController _taskController) {
taskController = _taskController;
}
public bool hasTaskControllerDefined {
get { return (taskController != null); }
}
private string nodeLineNumberListGenerationState {
get {
return (openNodeLines != null) + "&&" + (closeNodeLines != null);
}
}
public int nodeLineCount {
get {
int sum = 0;
if (openNodeLines != null) {
sum += openNodeLines.Count;
}
if (closeNodeLines != null) {
sum += closeNodeLines.Count;
}
return sum;
}
}
[SerializeField]
public FileHash __lineNumberListBtFileHashes = new FileHash();
public FileHash lineNumberListBtFileHashes {
get { return __lineNumberListBtFileHashes; }
private set { __lineNumberListBtFileHashes = value; }
}
public bool CheckLineNumberHashOutdated(bool allowNull = false) {
if (taskController != null) {
return lineNumberListBtFileHashes.HashChanged(taskController.generatedCodeBtFileHashes, allowNull);
}
Debug.LogWarning("CheckLineNumberHashOutdated: taskController is undefined!");
if (nodeLineNumberListError) {
return true;
}
return true;
}
public string GetLineNumberHashes () {
return $"codeHash: {lineNumberListBtFileHashes} fileHash:{taskController.generatedCodeBtFileHashes} ";
}
public void ClearLineNumberHashes() {
lineNumberListBtFileHashes.ClearHashCache();
}
private bool __nodeLineNumberListError = false;
public bool nodeLineNumberListError {
get { return __nodeLineNumberListError; }
private set { __nodeLineNumberListError = value; }
}
public bool nodeLineNumberListGenerated {
get {
if ((openNodeLines != null) && (closeNodeLines != null)) {
int tokenCount = taskController.tokens != null ?
taskController.tokens.Count : 0;
bool generated = (tokenCount == openNodeLines.Count) && (tokenCount == closeNodeLines.Count);
return generated;
}
else {
return false;
}
}
}
private void UpdateLineNumberHash() {
lineNumberListBtFileHashes.UpdateHashCache(taskController.generatedCodeBtFileHashes);
}
public void ResetNodeLinenumberList() {
if (taskController != null) {
if (taskController.generatorLogging) Debug.Log("CodeInspector.ResetNodeLinenumberList");
}
nodeLineNumberListError = false;
openNodeLines = null;
closeNodeLines = null;
ClearLineNumberHashes();
}
public new void SetDirty() {
if (!Application.isPlaying) {
if (taskController != null) {
EditorSceneManager.MarkSceneDirty(taskController.gameObject.scene);
EditorUtility.SetDirty(taskController);
taskController.SetBehaviourTreeDirty();
}
}
}
public void RegenerateNodeLineNumberListIfRequired (bool forceUpdate = false) {
if (CheckLineNumberHashOutdated() || forceUpdate) {
if (taskController.generatorLogging) {
Debug.LogWarning($"TaskController.Update: regenerating behaviour tree {GetLineNumberHashes()} {forceUpdate}");
}
ResetNodeLinenumberList();
GenerateNodeLineNumberList();
}
}
public void GenerateNodeLineNumberList() {
if (taskController) {
if (!taskController.generationError && !taskController.bindingError && CheckLineNumberHashOutdated() && !nodeLineNumberListGenerated) {
if (taskController.generatorLogging)
Debug.Log($"CodeInspector.GenerateNodeLineNumberList: running " +
$"(!{taskController.generationError}&&!{taskController.bindingError}&&{CheckLineNumberHashOutdated()}&&!{nodeLineNumberListGenerated})");
openNodeLines = new List<DictionaryOfIntAndNodeList>();
closeNodeLines = new List<DictionaryOfIntAndNodeList>();
foreach (NodeList treeRoots in taskController.treeRootNodes) {
// Debug.Log("Handling "+treeRoots);
DictionaryOfIntAndNodeList openNodeLine = new DictionaryOfIntAndNodeList();
DictionaryOfIntAndNodeList closeNodeLine = new DictionaryOfIntAndNodeList();
foreach (Node treeNode in treeRoots) {
// Debug.Log("- - "+treeNode);
foreach (Node childNode in treeNode.getAllNodesRecursively(false)) {
// Debug.Log("- - - "+childNode);
if (!openNodeLine.ContainsKey(childNode.lineNumber)) {
// Debug.Log("- - - new openNodeLine: "+childNode.lineNumber);
openNodeLine[childNode.lineNumber] = new NodeList();
}
openNodeLine[childNode.lineNumber].Add(childNode);
if (!closeNodeLine.ContainsKey(childNode.lastLineNumber)) {
// Debug.Log("- - - new closeNodeLine: "+childNode.lineNumber);
closeNodeLine[childNode.lastLineNumber] = new NodeList();
}
closeNodeLine[childNode.lastLineNumber].Add(childNode);
}
}
openNodeLines.Add(openNodeLine);
closeNodeLines.Add(closeNodeLine);
}
if (nodeLineNumberListGenerated) {
UpdateLineNumberHash();
// Debug.Log("CodeInspector.GenerateNodeLineNumberList: generated " + lineNumberListBtFileHashes);
}
else {
Debug.LogError("CodeInspector.GenerateNodeLineNumberList: failed" + lineNumberListBtFileHashes);
}
}
else {
if (taskController.generatorLogging)
Debug.Log($"CodeInspector.GenerateNodeLineNumberList: not run (!{taskController.generationError} && {CheckLineNumberHashOutdated()} &&! {nodeLineNumberListGenerated} )");
}
}
}
public void DrawCodeInspector() {
// string codeDisplayLog = "";
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
StringBuilder errorMessageSB = new StringBuilder();
if (taskController == null) {
errorMessageSB.Append("TaskController undefined! ");
}
else {
if (taskController.generationError) {
errorMessageSB.Append("generationError! ");
}
if (taskController.bindingError) {
errorMessageSB.Append("generationError! ");
}
if (!taskController.hasScript) {
errorMessageSB.Append("Missing Behaviour Tree Script! ");
}
}
if (nodeLineNumberListError) {
errorMessageSB.Append("nodeLineNumberListError! ");
}
if (errorMessageSB.Length > 0) {
GUI.contentColor = Color.red;
GUILayout.TextArea(errorMessageSB.ToString());
GUI.contentColor = Color.black;
}
// create a style based on the default label style5
GUIStyle myButtonStyle = new GUIStyle(GUI.skin.button);
// do whatever you want with this style, e.g.:
myButtonStyle.margin = new RectOffset(0, 0, -3, 0);
myButtonStyle.border = new RectOffset(0, 0, 0, 0);
myButtonStyle.overflow = new RectOffset(-4, -4, -4, -4);
myButtonStyle.fixedHeight = 25;
myButtonStyle.fixedWidth = 25;
// GUISkin guiSkin = (GUISkin)AssetDatabase.LoadAssetAtPath("Assets/Editor Default Resources/MyBTSkin.guiskin", typeof(GUISkin));
// if (guiSkin == null) {
// Debug.LogError($"GUISkin for BT not found MyBTSkin");
// }
// AssetDatabase.FindAssets("MyBTSkin") as GUISkin;
// GUISkin guiSkin = MyBtResources.myBtGuiSkin;
bool btSettingsChanged = false;
//Debug.Log($"--- CODE INSPECTOR --- {taskController.gameObject.name} --- {Time.frameCount} -------------------------------------");
if (taskController != null) {
if (nodeLineNumberListGenerated && !nodeLineNumberListError) {
// for "the number of task scripts"
for (int i = 0; i < taskController.taskScripts.Count; i++) {
if (taskController.taskScripts[i] != null) {
EditorGUILayout.LabelField(("_ Script _ " + taskController.taskScripts[i].name + " ").PadRight(120, '_'));
// EditorGUILayout.LabelField(taskController.taskScripts[i].name);
TokenList tokens = taskController.tokens[i];
DictionaryOfIntAndNodeList openNodeLine = openNodeLines[i];
DictionaryOfIntAndNodeList closeNodeLine = closeNodeLines[i];
// get the line number of the last token
int numberOfLines = tokens[tokens.Count - 1].location.line;
// codeDisplayLog += $"- numberOfLines {numberOfLines}\n";
int tokenIndex = 0;
// int nodeDepthCached = 0;
// bool foldCache = false;
// int foldAbove = int.MaxValue;
Node lastNodeCache = null;
Node thisNode = null;
bool isFolded = false;
StringBuilder sbLineText = new StringBuilder(200);
string lineText;
StringBuilder sbLineNumber = new StringBuilder(4);
string lineNumberText;
NodeList logNodes = new NodeList();
bool isClosing; // isOpening
int nodeDepth = 0;
for (int currentLineNumber = 1; currentLineNumber < numberOfLines + 1; currentLineNumber++) {
// codeDisplayLog += $"iterate line {currentLineNumber}\n";
sbLineNumber.Clear();
lineNumberText = sbLineNumber.AppendFormat("{0:D4}", tokens[tokenIndex].location.line).ToString();
// lineNumberText = String.Format("{0:D4}", tokens[tokenIndex].location.line);
thisNode = null;
isClosing = false;
// isOpening = false;
if (closeNodeLine.ContainsKey(currentLineNumber)) {
lastNodeCache = closeNodeLine[currentLineNumber][0];
isClosing = true;
nodeDepth -= 1;
}
// generate text of code to display by iterating trough the tokens
sbLineText.Clear();
if (nodeDepth >= 0)
sbLineText.Append(' ', nodeDepth * 4);
while ((tokenIndex < tokens.Count) && (tokens[tokenIndex].location.line == currentLineNumber)) {
sbLineText.Append(tokens[tokenIndex].location.code.Replace("\r", "").Replace("\n", "").Replace("\t", "").TrimStart(' ').TrimEnd(' '));
tokenIndex += 1;
// Debug.Log("tokenIndex "+tokenIndex + " on line "+ currentLineNumber + " " + s);
}
lineText = sbLineText.ToString(); // + $"{nodeDepth}";
// find depth of current Node
if (openNodeLine.ContainsKey(currentLineNumber)) {
lastNodeCache = openNodeLine[currentLineNumber][0];
thisNode = openNodeLine[currentLineNumber][0];
if (thisNode != null) {
if ((thisNode.GetType() == typeof(CompositeNode))
|| (thisNode.GetType() == typeof(TreeNode))
|| (thisNode.GetType() == typeof(DecoratorNode))
) {
// isOpening = true;
nodeDepth += 1;
}
}
// thisnode is a weakreference, and is deleted externally on script change
// https://docs.unity3d.com/Manual/script-Serialization.html
else {
// the object has been destroyed
Debug.LogWarning($"DrawCodeInspector: detected error on line {currentLineNumber} -> clearing nodelinenumberlists");
//requireResetNodeLinenumberList = true;
nodeLineNumberListError = true;
}
}
// if this line contains a node, we fold it when the parent is folded. because a composite node would be invisible if it hides itself.
isFolded = (lastNodeCache != null) ? lastNodeCache.parentFolded : false;
// this node is undefined, it is a comment, a closing bracket or a empty line. in this case use the lastNodecache.isFolded, as we are surely not a composite node
if (thisNode == null) {
// dont fold closing brackets
if (isClosing) {
if (lastNodeCache != null) {
if (lastNodeCache.parentNode != null) {
isFolded = lastNodeCache.parentFolded;
}
}
}
else {
if (lastNodeCache != null) {
// fold comments in composites
isFolded = lastNodeCache.isFolded || isFolded;
}
}
if (nodeDepth == 0) {
isFolded = false;
}
}
if (!isFolded) {
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField(lineNumberText, GUILayout.Width(32));
// dont show fold on ActionNode & RunTreeNode
if ((thisNode != null) && (thisNode.GetType() != typeof(ActionNode)) && (thisNode.GetType() != typeof(RunTreeNode))) {
bool newIsFolded = EditorGUILayout.Toggle(thisNode.isFolded, MyBtResources.popoutToggleGuiStyle, GUILayout.MaxWidth(16)); // label,
if (newIsFolded != thisNode.isFolded) {
thisNode.isFolded = newIsFolded;
btSettingsChanged |= true;
}
}
else {
EditorGUILayout.LabelField("", MyBtResources.emptyGuiStyle, GUILayout.Width(MyBtResources.defaultGuiIconWidth), GUILayout.Height(MyBtResources.defaultGuiHeight));
}
EditorGUILayout.LabelField(lineText, GUILayout.ExpandWidth(true), GUILayout.Height(MyBtResources.defaultGuiHeight));
// logNodes = new NodeList();
logNodes.Clear();
// list of nodes that should show logging
if (thisNode != null) {
foreach (Node lineNode in openNodeLine[currentLineNumber]) {
if ((lineNode != null) && (lineNode.nodeRuntimeDataList != null)) {
//foreach (NodeRuntimeData runtimeData in lineNode.nodeRuntimeDataList) {
// if (runtimeData != null) {
// //GUI.color = MyBtResources.NodeStateColors[runtimeData.nodeState];
// //EditorGUILayout.LabelField("", MyBtResources.NodeStateGuiStyle[runtimeData.nodeState], GUILayout.Width(defaultGuiIconWidth), GUILayout.Height(defaultGuiHeight));
// GUI.color = MyBtResources.NodeResultColors[runtimeData.nodeResult];
// EditorGUILayout.LabelField($"R{lineNode.nodeRuntimeDataList.Count}", MyBtResources.NodeResultGuiStyle[runtimeData.nodeResult], GUILayout.Width(defaultGuiIconWidth), GUILayout.Height(defaultGuiHeight));
// }
//}
//Debug.Log(lineNode.NodeLogger("DEBUG", ""));
if ((lineNode.lastResults != null) && (taskController.UiChangedAt(lineNode.lastResultsTime))) {
for (int j=0; j< lineNode.lastResults.Count; j++) {
foreach (NodeResult nrs in lineNode.lastResults[j]) {
GUI.color = MyBtResources.NodeResultColors[nrs];
EditorGUILayout.LabelField("", MyBtResources.NodeResultGuiStyle[nrs], GUILayout.Width(MyBtResources.defaultGuiIconWidth), GUILayout.Height(MyBtResources.defaultGuiHeight));
}
//for (int k = 0; k < lineNode.lastResults[j].Count; k++) {
//GUI.color = MyBtResources.NodeResultColors[lineNode.lastResults[j][k]];
//EditorGUILayout.LabelField("", MyBtResources.NodeResultGuiStyle[lineNode.lastResults[j][k]], GUILayout.Width(defaultGuiIconWidth), GUILayout.Height(defaultGuiHeight));
//}
//lineNode.lastResults[j] = NodeResult.Undefined;
}
//foreach (NodeResult lastResult in lineNode.lastResults) {
//}
}
else {
EditorGUILayout.LabelField("", MyBtResources.nodeUninitializedState, GUILayout.Width(MyBtResources.defaultGuiIconWidth), GUILayout.Height(MyBtResources.defaultGuiHeight));
}
GUI.color = lineNode.logStringDisplay ? Color.yellow : Color.white;
bool newLogStringDisplay = EditorGUILayout.Toggle(lineNode.logStringDisplay, MyBtResources.logStringToggleGuiStyle, GUILayout.Width(MyBtResources.defaultGuiIconWidth), GUILayout.Height(MyBtResources.defaultGuiHeight));
if (newLogStringDisplay != lineNode.logStringDisplay) {
lineNode.logStringDisplay = newLogStringDisplay;
btSettingsChanged |= true;
}
if (lineNode.logStringDisplay || lineNode.debugChangesActive) {
logNodes.Add(lineNode);
}
if (taskController.runtimeLogging) {
GUI.color = lineNode.debugChangesActive ? Color.cyan : Color.white;
bool newDebugChangesActive = EditorGUILayout.Toggle(lineNode.debugChangesActive, MyBtResources.alertToggleGuiStyle, GUILayout.Width(MyBtResources.defaultGuiIconWidth), GUILayout.Height(MyBtResources.defaultGuiHeight));
if (newDebugChangesActive != lineNode.debugChangesActive) {
lineNode.debugChangesActive = newDebugChangesActive;
btSettingsChanged |= true;
}
GUI.color = lineNode.debugInternalActive ? Color.red : Color.white;
bool newDebugInternalActive = EditorGUILayout.Toggle(lineNode.debugInternalActive, MyBtResources.debugToggleGuiStyle, GUILayout.Width(MyBtResources.defaultGuiIconWidth), GUILayout.Height(MyBtResources.defaultGuiHeight));
if (newDebugInternalActive != lineNode.debugInternalActive) {
lineNode.debugInternalActive = newDebugInternalActive;
btSettingsChanged |= true;
}
}
}
}
GUI.color = Color.white;
}
else {
// to fix layout lines with a comment
Rect iconRect2 = GUILayoutUtility.GetRect(GUIContent.none, new GUIStyle(), new GUILayoutOption[] { GUILayout.Width(MyBtResources.defaultGuiIconWidth), GUILayout.Height(MyBtResources.defaultGuiHeight) });
iconRect2.height += 2;
iconRect2.y += 1;
iconRect2.x += 1;
}
EditorGUILayout.EndHorizontal();
if (logNodes.Count > 0) {
foreach (Node logNode in logNodes) {
foreach (NodeRuntimeData runtimeData in logNode.nodeRuntimeDataList) {
if (runtimeData != null) {
GUI.color = Color.yellow;
if (logNode.logStringDisplay) {
if (logNode is ActionNode) {
EditorGUILayout.BeginHorizontal();
string logString = (runtimeData.logString != null) ? runtimeData.logString.ToString().Replace("\n", "\\n") : "";
EditorGUILayout.LabelField("logs: " + logString);
EditorGUILayout.EndHorizontal();
EditorGUILayout.BeginHorizontal();
ActionNodeRuntimeData anrd = (runtimeData as ActionNodeRuntimeData);
if (anrd != null) {
string userDataString = (anrd.userData != null) ? anrd.userData.ToString().Replace("\n", "\\n") : "";
EditorGUILayout.LabelField("data: " + userDataString);
}
EditorGUILayout.EndHorizontal();
}
}
if (logNode.debugChangesActive) {
EditorGUILayout.BeginHorizontal();
string statusString = $"state={runtimeData.nodeState} result={runtimeData.nodeResult}";
if (logNode is CompositeNode) {
statusString += $" curIndex={runtimeData.currentIndex}";
}
EditorGUILayout.LabelField("" + statusString);
EditorGUILayout.EndHorizontal();
}
GUI.color = Color.white;
}
}
}
}
}
}
}
}
}
else {
string logMessage = $"CodeInspector.DrawCodeInspector: not drawing ({nodeLineNumberListGenerated}) ({nodeLineNumberListGenerationState})";
GUI.contentColor = Color.red;
EditorGUILayout.LabelField(logMessage);
GUI.contentColor = Color.black;
}
if (btSettingsChanged) {
SetDirty();
}
// GUILayout.Label(codeDisplayLog);
}
EditorGUILayout.EndVertical();
}
#endif
}
}