| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201 | using System.Collections.Generic;using System.Linq;using GraphProcessor;using Debug = UnityEngine.Debug;namespace NodeGraphProcessor.Examples{    public class ConditionalProcessor : BaseGraphProcessor    {        List< BaseNode >		processList;        List< StartNode >		startNodeList;        Dictionary<BaseNode, List<BaseNode>>    nonConditionalDependenciesCache = new Dictionary<BaseNode, List<BaseNode>>();        public bool             pause;        public IEnumerator<BaseNode> currentGraphExecution { get; private set; } = null;        // static readonly float   maxExecutionTimeMS = 100; // 100 ms max execution time to avoid infinite loops        /// <summary>        /// Manage graph scheduling and processing        /// </summary>        /// <param name="graph">Graph to be processed</param>        public ConditionalProcessor(BaseGraph graph) : base(graph) {}        public override void UpdateComputeOrder()        {            // Gather start nodes:            startNodeList = graph.nodes.Where(n => n is StartNode).Select(n => n as StartNode).ToList();            // In case there is no start node, we process the graph like usual            if (startNodeList.Count == 0)            {                processList = graph.nodes.OrderBy(n => n.computeOrder).ToList();            }            else            {                nonConditionalDependenciesCache.Clear();                // Prepare the cache of non-conditional node execution            }        }        public override void Run()        {            IEnumerator<BaseNode> enumerator;            if(startNodeList.Count == 0)            {                enumerator = RunTheGraph();            }            else            {                Stack<BaseNode> nodeToExecute = new Stack<BaseNode>();                // Add all the start nodes to the execution stack                startNodeList.ForEach(s => nodeToExecute.Push(s));                // Execute the whole graph:                enumerator = RunTheGraph(nodeToExecute);            }            while(enumerator.MoveNext())                ;        }                private void WaitedRun(Stack<BaseNode> nodesToRun)        {            // Execute the waitable node:            var enumerator = RunTheGraph(nodesToRun);            while(enumerator.MoveNext())                ;        }        IEnumerable<BaseNode> GatherNonConditionalDependencies(BaseNode node)        {            Stack<BaseNode> dependencies = new Stack<BaseNode>();            dependencies.Push(node);                    while (dependencies.Count > 0)            {                var dependency = dependencies.Pop();                foreach (var d in dependency.GetInputNodes().Where(n => !(n is IConditionalNode)))                    dependencies.Push(d);                if (dependency != node)                    yield return dependency;            }        }                private IEnumerator<BaseNode> RunTheGraph()        {            int count = processList.Count;            for(int i = 0; i < count; i++)            {                processList[i].OnProcess();                yield return processList[i];            }        }        	    private IEnumerator<BaseNode> RunTheGraph(Stack<BaseNode> nodeToExecute)		{			HashSet<BaseNode> nodeDependenciesGathered = new HashSet<BaseNode>();			HashSet<BaseNode> skipConditionalHandling  = new HashSet<BaseNode>();				while(nodeToExecute.Count > 0)			{				var node = nodeToExecute.Pop();				// TODO: maxExecutionTimeMS					// In case the node is conditional, then we need to execute it's non-conditional dependencies first				if(node is IConditionalNode && !skipConditionalHandling.Contains(node))				{					// Gather non-conditional deps: TODO, move to the cache:					if(nodeDependenciesGathered.Contains(node))					{						// Execute the conditional node:						node.OnProcess();						yield return node;							// And select the next nodes to execute:						switch(node)						{							// special code path for the loop node as it will execute multiple times the same nodes							case ForLoopNode forLoopNode:								forLoopNode.index = forLoopNode.start - 1; // Initialize the start index								foreach(var n in forLoopNode.GetExecutedNodesLoopCompleted())									nodeToExecute.Push(n);								for(int i = forLoopNode.start; i < forLoopNode.end; i++)								{									foreach(var n in forLoopNode.GetExecutedNodesLoopBody())										nodeToExecute.Push(n);										nodeToExecute.Push(node); // Increment the counter								}									skipConditionalHandling.Add(node);								break;							// another special case for waitable nodes, like "wait for a coroutine", wait x seconds", etc.							case WaitableNode waitableNode:								foreach(var n in waitableNode.GetExecutedNodes())									nodeToExecute.Push(n);									waitableNode.onProcessFinished += (waitedNode) =>								{									Stack<BaseNode> waitedNodes = new Stack<BaseNode>();									foreach(var n in waitedNode.GetExecuteAfterNodes())										waitedNodes.Push(n);									WaitedRun(waitedNodes);									waitableNode.onProcessFinished = null;								};								break;							case IConditionalNode cNode:								foreach(var n in cNode.GetExecutedNodes())									nodeToExecute.Push(n);								break;							default:								Debug.LogError($"Conditional node {node} not handled");								break;						}							nodeDependenciesGathered.Remove(node);					}					else					{						nodeToExecute.Push(node);						nodeDependenciesGathered.Add(node);						foreach(var nonConditionalNode in GatherNonConditionalDependencies(node))						{							nodeToExecute.Push(nonConditionalNode);						}					}				}				else				{					node.OnProcess();					yield return node;				}			}		}        // Advance the execution of the graph of one node, mostly for debug. Doesn't work for WaitableNode's executeAfter port.        public void Step()        {            if (currentGraphExecution == null)            {	            Stack<BaseNode> nodeToExecute = new Stack<BaseNode>();	            if(startNodeList.Count > 0)		            startNodeList.ForEach(s => nodeToExecute.Push(s));	            currentGraphExecution = startNodeList.Count == 0 ? RunTheGraph() : RunTheGraph(nodeToExecute);	            currentGraphExecution.MoveNext(); // Advance to the first node            }            else            if (!currentGraphExecution.MoveNext())                currentGraphExecution = null;        }    }}
 |