Search code examples
c#unity-game-engineasynchronousscheduled-tasks

Scheduling async method execution


I have a game that computes player actions on a remote system. For the sake of argument let's say this system can work only on one action at a time, but the player should be able to queue as many actions as they like.

Whenever the player performs an action, the client sends a request to remote:

async void ActionAsync(){

string id = await ActionRequestToRemote();
CharacterActions.Add(new CharacterAction(id, currentlySelectedGameobject));      
}

Getting the id is quick enough, but afterwards it takes remote about 6sec to come back with a new game state, during which all further await requests go into oblivion (say, the player clicks 3 times within a second).

What would be the best way to let the player keep queuing actions, while stalling/scheduling the execution of await ActionARequestToRemote(id) until the previous one has finished running?

Whenever remote processing is finished, following method is invoked to start playing animations etc.

void UpdateLocalGameState(string Type, bool success)
    {
        if (success)
        {
            switch (type)
            {
                case "ActionA":
                    LocallyDoActionA(CharacterActions[0]);
                    break;
                case "ActionB":
                    LocallyDoActionB(CharacterActions[0]);
                    break;
                case "ActionC":
                    LocallyDoActionC(CharacterActions[0]);
                    break;
            }
        }
    }

I currently work through the List recusrively to dequeue itself, ignoring requests at the time of player click, and just resending them after execution of all tasks has finished. But that seems inelegant and like there may be a better way?


Solution

  • Sounds to me like you want to use some sort of ConcurrentQueue and allow actions to pile up there.

    Something like e.g.

    public readonly struct ActionResult
    {
        public string Type { get; }
        public bool Success { get; }
    
        public ActionResult(string type, bool success)
        {
            Type = type;
            Success = success;
        }
    }
    
    private readonly ConcurrentQueue<CharacterAction> CharacterActions = new ();
    private bool isHandlingQueuedActions;
    
    public event Action<ActionResult> onActionResult;
    
    public void SendActionQueued(CharacterAction action)
    {
        CharacterActions.Enqueue(action);
    
        if(!isHandlingQueuedActions)
        {
            Task.Run(async ()=> await HandleQueuedActions());
        }
    }
    
    private async Task HandleQueuedActions()
    {
        isHandlingQueuedActions = true;
    
        while(CharacterActions.TryDequeue(out var action)) 
        {
            ActionResult result = await HoweverYouSubmitTheAction(action);
            onActionResult?.Invoke(result);
        }
    
        isHandlingQueuedActions = false;
    }