public async Task <ActionResult> ProcessAllAsync(object UserData = null, int MaxUnwindDepth = 0, bool one = false) { LastActionCancelled = false; bool DoneOne = false; while (!Paused && !IsBlockEmpty && !LastActionCancelled && !DoneOne) { if (one) { DoneOne = true; } // Get next action and make sure it's up to date if cloned from another game var action = Queue.RemoveFront(); action.Game = Game; if (action.Source.Game.GameId != Game.GameId) { action.Source = Game.Entities[action.Source.Id]; } #if _QUEUE_DEBUG DebugLog.WriteLine("Queue (Game " + Game.GameId + "): Dequeued action " + action + " for " + action.Source.ShortDescription + " at depth " + Depth); #endif // Get needed arguments for action from stack action.Args = new ActionResult[action.Action.Args.Count]; for (int i = action.Action.Args.Count - 1; i >= 0; i--) { // Prefer PreCompiledQueueAction items as arguments var arg = action.Action.CompiledArgs[i]; if (!arg.HasResult) { // Otherwise prefer EagerQueueAction items as arguments if (action.Action.EagerArgs[i] != null) { arg = action.Action.EagerArgs[i].Run(Game, action.Source, null); } else { // Otherwise use the ResultStack to get regular QueueAction items as arguments // In this round, only pop arguments for which ActionGraph parameters were supplied // to the QueueAction as these will be at the top of the stack if (action.Action.Args[i] != null) { arg = StackPop(); List <IEntity> eList = arg; if (eList != null && eList.Count > 0 && eList[0].Game != Game) { arg = new List <IEntity>(eList.Select(e => Game.Entities[e.Id])); } } } } action.Args[i] = arg; } // Now go through all of the arguments that weren't supplied as ActionGraphs and pop them off the stack for (int i = action.Action.Args.Count - 1; i >= 0; i--) { if (action.Action.Args[i] == null) { var arg = StackPop(); List <IEntity> eList = arg; if (eList != null && eList.Count > 0 && eList[0].Game != Game) { arg = new List <IEntity>(eList.Select(e => Game.Entities[e.Id])); } action.Args[i] = arg; } } // Replace current UserData with new UserData if supplied if (UserData != null) { this.UserData = UserData; } action.UserData = this.UserData; if (OnActionStarting != null) { OnActionStarting(this, action); if (action.Cancel) { LastActionCancelled = true; continue; } } var actionType = action.Action.GetType(); if (ReplacedActions.ContainsKey(actionType)) { await ReplacedActions[actionType](this, action); // action.Cancel implied when action is replaced LastActionCancelled = true; continue; } if (HasHistory) { AddItem(action); } // Run action and push results onto stack #if _QUEUE_DEBUG DebugLog.WriteLine("Queue (Game " + Game.GameId + "): Running action " + action + " for " + action.Source.ShortDescription + " at depth " + Depth); #endif var @async = action.Action as QueueActionAsync; var result = (@async != null ? await @async.RunAsync(action.Game, action.Source, action.Args) : action.Action.Run(action.Game, action.Source, action.Args)); if (result.HasResult) { StackPush(result); } // The >= allows the current block to unwind for ProcessBlock() while (IsBlockEmpty && Depth >= MaxUnwindDepth) { EndBlock(); if (IsEmpty) { break; } } #if _QUEUE_DEBUG DebugLog.WriteLine("Queue (Game " + Game.GameId + "): Finished action " + action + " for " + action.Source.ShortDescription + " at depth " + Depth); #endif OnAction?.Invoke(this, action); // Propagate cancellation up the chain by only changing it if not already set LastActionCancelled = action.Cancel; } // Return whatever is left on the stack if (LastActionCancelled) { return(ActionResult.None); } var finalResult = ResultStack.Reverse().FirstOrDefault(); if (!Paused && IsEmpty) { StackClear(); } return(finalResult); }