UP-Viagg-io/Viagg-io/Assets/Packages/MyBT/BT/BehaviourTreeGenerator.cs

359 lines
14 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.Linq;
using System.Security.Cryptography;
using System.Text;
using UnityEngine;
namespace MyBT {
[System.Serializable]
public class BehaviourTreeGenerator : ScriptableObject {
// void Init() {
// }
#region generated stuff
// the contents of the script
// [HideInInspector, SerializeField]
// public List<string> codes = null;
// list of the tasks
[SerializeField]
public DictionaryOfStringAndDictionaryOfStringAndMethodImplementation methods = null;
// the tokens are generated by the TokenEnvironment
[SerializeField]
public List<TokenList> tokens = null;
// the treenodes are generated by the NodeGenerator
[SerializeField]
public List<NodeList> treeRootNodes = null;
[SerializeField]
public TreeNode rootNode = null;
#endregion
#region settings
// [SerializeField]
// public List<UnityEngine.TextAsset> taskScripts = new List<UnityEngine.TextAsset>();
// currently causes errors, thus it's disabled in the editor
[HideInInspector]
public bool readUnityComponents = false;
[SerializeField]
public bool generatorLogging = false;
public bool internalAutomaticRegenerate = true;
#endregion
#region runtime variables
public bool generationError = false;
public bool bindingError = false;
#endregion
#region file change detection
// cache of the file hashes as the currently are
[SerializeField]
public FileHash textAssetHashes = new FileHash();
// stores the file hashes at the moment of generation
[SerializeField]
public FileHash generatedCodeBtFileHashes = new FileHash();
// stores the file hashes at the moment of binding
[SerializeField]
public FileHash boundCodeBtFileHashes = new FileHash();
#endregion
public bool HasScripts (TaskController _taskController) {
return (_taskController.taskScripts != null) && (_taskController.taskScripts.Count > 0);
}
public bool hasRootNode {
get {
return (rootNode != null);
}
}
public void FullGenerate (TaskController taskController, bool force) {
GenerateTokensAndNodes(taskController, force);
ScanAndBindMethods(taskController, force);
SetTaskControllerAndGenerateCache(taskController);
}
public void FullReset() {
ClearBinding();
ResetNodesAndTokens();
}
public void ClearBindingsAndScanAndBindMethods (TaskController taskController, bool force) {
ClearBinding();
ScanAndBindMethods(taskController, force);
}
public void BtScriptChangeDetected(TaskController _taskController, bool forceUpdate = false) {
if (generatorLogging) Debug.Log("BehaviourTreeGenerator.BtScriptChangeDetected");
// update current script hashes, so we compare the generated nodes against the actual scripts
textAssetHashes.UpdateHashCacheFromTextAsset(_taskController.taskScripts);
if (CheckGenerationOutdated() || forceUpdate) {
if (generatorLogging) Debug.Log("BehaviourTreeGenerator.BtScriptChangeDetected: clear nodes and tokens");
ResetNodesAndTokens();
}
if (CheckBindingOutdated(_taskController) || forceUpdate) {
if (generatorLogging) Debug.Log("BehaviourTreeGenerator.BtScriptChangeDetected: clear binding");
ClearBinding();
}
// regenerate
GenerateTokensAndNodes(_taskController);
ScanAndBindMethods(_taskController, forceUpdate);
_taskController.InspectorRegenerate?.Invoke(true);
}
public void SetTaskControllerAndGenerateCache (TaskController _taskController) {
if (treeRootNodes != null) {
foreach (NodeList rn in treeRootNodes) {
foreach (Node crn in rn) {
foreach (Node cn in crn.getAllNodesRecursively(false)) {
cn.SetTaskController(_taskController);
cn.GenerateNodeDataCaches();
}
}
}
}
else {
Debug.LogError($"BehaviourtreeGenerator.SetTaskController: treeRootNodes {treeRootNodes}");
}
}
public void Restart() {
if (rootNode != null) {
if (rootNode.nodeRuntimeDataList.Count > 0) {
rootNode.nodeRuntimeDataList[0].Destroy();
}
}
}
public void RegenerateBehaviourTreeIfRequired(TaskController _taskController, bool forceUpdate = false) {
if (CheckGenerationOutdated() || CheckBindingOutdated(_taskController) || forceUpdate) {
if (generatorLogging) {
Debug.LogWarning($"TaskController.Update: regenerating behaviour tree {CheckGenerationOutdated()} {CheckBindingOutdated(_taskController)}");
}
FullReset();
}
else {
Debug.Log($"TaskController.Update: status: generated:{generatedCodeBtFileHashes} bound:{boundCodeBtFileHashes} text:{textAssetHashes}");
}
if (CheckGenerationOutdated() || CheckBindingOutdated(_taskController) || forceUpdate) { // || !behaviourTreeGenerator.isBound // this makes it way slower...
if (generatorLogging) {
Debug.LogWarning($"TaskController.TickUpdate: updating bindings");
}
FullGenerate(_taskController, false);
}
else {
Debug.Log($"TaskController.Update: status: generated:{generatedCodeBtFileHashes} bound:{boundCodeBtFileHashes} text:{textAssetHashes}");
}
}
// public bool tokensGenerated {
// get {
// return Tokenizer.IsAllGenerated(tokens);
// }
// }
// public bool nodesGenerated {
// get {
// return NodeGenerator.IsAllGenerated(treeRootNodes, rootNode);
// }
// }
// public bool isGenerated {
// get {
// return (hasScript && tokensGenerated && nodesGenerated);
// }
// }
// public string generationState {
// get {
// return (hasScript + "&&" + tokensGenerated + "&&" + nodesGenerated);
// }
// }
#region Tokenizer & NodeGenerator
public bool CheckGenerationOutdated(bool allowNull = false) {
// if (debugLog) Debug.Log("CheckGenerationOutdated:\tGenerated Hash:\t" + generatedCodeBtFileHashes + " \n"+
// "\t\t\tText Asset Hash:\t" + textAssetHashes);
return generatedCodeBtFileHashes.HashChanged(textAssetHashes, allowNull);
}
public string GetGenerationHashes () {
return $"text:{textAssetHashes} generated:{generatedCodeBtFileHashes}";
}
public void UpdateGenerationHashes() {
if (generatorLogging) Debug.Log("BehaviourTreeGenerator.UpdateGenerationHashes");
generatedCodeBtFileHashes.UpdateHashCache(textAssetHashes);
}
public void ClearGenerationHashes() {
if (generatorLogging) Debug.Log("BehaviourTreeGenerator.ClearGenerationHashes");
generatedCodeBtFileHashes.ClearHashCache();
}
public void GenerateTokensAndNodes(TaskController _taskController, bool force=false) {
if (generatorLogging) Debug.Log($"BehaviourTreeGenerator.Generate ({CheckGenerationOutdated()} && !{generationError} && {internalAutomaticRegenerate} )");
if (HasScripts(_taskController) && CheckGenerationOutdated() && !generationError && (internalAutomaticRegenerate || force)) {
// generate the tokens
if (generatorLogging) Debug.Log($"BehaviourTreeGenerator.Generate: --- Execute Tokenizer --- ({HasScripts(_taskController)} && ({GetGenerationHashes()}) !{generationError} && ({internalAutomaticRegenerate} || {force}))");
bool tokenizerSuccess = Tokenizer.Tokenize(out tokens, _taskController.taskScripts);
generationError |= !tokenizerSuccess;
if (!tokenizerSuccess) {
Debug.LogError($"BehaviourTreeGenerator.Generate: Tokenizer not generated ");
}
bool nodeGenerationSuccess = NodeGenerator.Generate(out treeRootNodes, out rootNode, tokens);
generationError |= !nodeGenerationSuccess;
if (!nodeGenerationSuccess) {
Debug.LogError("BehaviourTreeGenerator.Generate: failed");
}
// assign taskController
SetTaskControllerAndGenerateCache(_taskController);
// update hash to save last successful generation source
if (generationError) {
Debug.LogError($"BehaviourTreeGenerator.Generate: NodeGenerator not generated ");
ClearGenerationHashes();
}
else {
UpdateGenerationHashes();
}
}
}
public int nodeCount {
get { return NodeGenerator.GeneratedSize(treeRootNodes); }
}
/// <summary>
/// Reset the whole Task / Node / Action Tree structure for regeneration from the source behaviour tree.
/// </summary>
public void ResetNodesAndTokens() {
if (generatorLogging) Debug.LogWarning("BehaviourTreeGenerator.ResetNodesAndTokens");
if (generatorLogging) Debug.Log("BehaviourTreeGenerator.Reset: --- Reset NodeGenerator ---");
NodeGenerator.DestroyAll(ref treeRootNodes, ref rootNode);
if (generatorLogging) Debug.Log("BehaviourTreeGenerator.Reset: --- Reset Tokenizer ---");
Tokenizer.DestroyAll(ref tokens);
generationError = false;
ClearGenerationHashes();
}
#endregion
// public bool methodsScanned {
// get {
// return MethodScanner.IsGenerated(methods);
// }
// }
// public bool methodsBound {
// get {
// return MethodBinder.IsAllBound(treeRootNodes);
// }
// }
// public bool isBound {
// get {
// return (methodsScanned && methodsBound);
// }
// }
#region Binding
public bool CheckBindingOutdated(TaskController _taskController, bool allowNull = false) {
textAssetHashes.UpdateHashCacheFromTextAsset(_taskController.taskScripts);
return boundCodeBtFileHashes.HashChanged(textAssetHashes, allowNull);
}
public void UpdateBindingHashes() {
if (generatorLogging) Debug.Log("BehaviourTreeGenerator.UpdateBindingHashes");
boundCodeBtFileHashes.UpdateHashCache(textAssetHashes);
}
public void ClearBindingHashes() {
if (generatorLogging) Debug.Log("BehaviourTreeGenerator.ClearBindingHashes");
boundCodeBtFileHashes.ClearHashCache();
}
public void VerifyBinding (TaskController _taskController) {
if (generatorLogging) Debug.Log("BehaviourTreeGenerator.VerifyBinding");
if (!MethodBinder.VerifyBinding(treeRootNodes)) {
ClearBindingHashes();
RegenerateBehaviourTreeIfRequired(_taskController);
}
}
public void ScanAndBindMethods(TaskController _taskController, bool force = false) {
if (CheckBindingOutdated(_taskController) && !bindingError && internalAutomaticRegenerate || force) {
if (generatorLogging) Debug.Log($"BehaviourTreeGenerator.ScanAndBindMethods: ({CheckBindingOutdated(_taskController)} && !{bindingError} && {internalAutomaticRegenerate} || {force}) ");
// read the contents of the script, remove tabs and spaces
bool componentCheckSuccess = MethodScanner.CheckComponents(_taskController.gameObject, readUnityComponents, ref methods);
bindingError |= !componentCheckSuccess;
if (!componentCheckSuccess) {
if (generatorLogging) Debug.LogError("BehaviourTreeGenerator.ScanAndBindMethods: MethodScanner failed");
}
bool methodBinderSuccess = MethodBinder.Bind(ref treeRootNodes, rootNode, methods);
bindingError |= !methodBinderSuccess;
if (!methodBinderSuccess) {
if (generatorLogging) Debug.LogError("BehaviourTreeGenerator.ScanAndBindMethods: MethodBinder failed");
}
SetTaskControllerAndGenerateCache(_taskController);
if (!bindingError) {
if (generatorLogging) Debug.Log($"BehaviourTreeGenerator.ScanAndBindMethods: --- UpdateBindingHashes ---");
UpdateBindingHashes();
}
else {
Debug.Log($"BehaviourTreeGenerator.ScanAndBindMethods: ScanAndBindMethods Failed");
ClearBindingHashes();
}
}
else {
if (generatorLogging) Debug.LogWarning($"BehaviourTreeGenerator.Bind: not running ( {CheckBindingOutdated(_taskController)} && !{generationError} && {internalAutomaticRegenerate} )");
}
}
public void ClearBinding() {
if (generatorLogging) Debug.Log("BehaviourTreeGenerator.ClearBinding: --- Reset MethodBinder ---");
MethodBinder.ResetAll(ref treeRootNodes); // causes error in building
if (generatorLogging) Debug.Log("BehaviourTreeGenerator.ClearBinding: --- Reset MethodScanner ---");
MethodScanner.Reset(ref methods);
bindingError = false;
ClearBindingHashes();
}
#endregion
}
}