C# Await ToSignal Unreliable with Custom Nodes

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By dhav211

Hello, I’ve been working on a quick project, a very simple rougelike with Godot and C#.

I’ve had a few hiccups here and there, but this one has really been making my head scratch and I feel like I should fully understand it before I move forward.

I’ve been having issues with the await keyword. Now, it works absolutely flawless with the timer and it’s timeout() signal, but when I’m using a custom signal that’s where I start having problems. I will post my code here in a second but the issue I’m having is the await tosignal keyword is unreliable in a highly predictable manner. I know I’m just not fully understanding the the underlying process here!

My goal is a turn based system, where when it’s the players turn, he selects a grid position to move to, saves the path, then moves one spot. When he moves one spot, the turn complete signal is fired. This works once, and the next time it won’t. Here, I’ll post the code.

– From TurnManager.cs

while (isTurnManagerRunning)
    {
        for (int i = 0; i < turns.Count; i++)
        {
            currentTurn = turns[i];
            if (turns[i] is Player)
            {
                GD.Print("-----------It's the Player's turn");

                if (player == null)
                    player = turns[i] as Player;
                
                player.StartTurn();
                await ToSignal(player, "turn_completed");
                GD.Print("Starting turn timer");
                timer.Start();
                await ToSignal(timer, "timeout");
                GD.Print("The player's turn has finished");
                
            }
            else if (turns[i] is Enemy)
            {
                GD.Print("-----------It's the Enemy's turn");
                enemy = turns[i] as Enemy;
                enemy.RunAI();
                enemy = null;
                GD.Print("Starting turn timer");
                timer.Start();
                await ToSignal(timer, "timeout");
                GD.Print("The Enemy's turn has finished");
            }
        }
    }

–From Player.cs

    public override void _Input(InputEvent @event)
{
    if (@event is InputEventMouseButton mouseInput)
    {

        if (mouseInput.IsActionPressed("ui_left_click"))
        {
            GD.Print("Left click");
            SetPathToFollow(mouseInput);
            MovePlayerToNextPosition();
        }
        else if (mouseInput.IsActionPressed("ui_right_click"))
        {
            CancelPathToFollow(mouseInput);
        }
    }
}

public void StartTurn()
{
    GD.Print("Running StartTurn method");
    ChangeGridPosition();
    MovePlayerToNextPosition();
}

private void MovePlayerToNextPosition()
{
    if (pathToFollow.Count > 0 && turnManager.CurrentTurn == this)
    {
        Position = new Vector2(pathToFollow[0].x * 16, pathToFollow[0].y * 16);
        ChangeGridPosition();
        pathToFollow.Remove(pathToFollow[0]);
        EmitSignal(nameof(turn_completed));
        GD.Print("The player has moved");
    }

    if (pathToFollow.Count == 0)
    {
        GD.Print("There is no path to follow");
    }

    if (turnManager.CurrentTurn != this)
    {
        GD.Print("It isn't the player's turn to move");
    }
}

Sorry it’s not the prettiest code, I’m just trying to get it function at this point. I would copy and paste the output console but I can’t seem to be able to do that, as far as I know anyways.

So if anyone has made it this far, could anyone explain to me what this await tosignal really expects? Does it just proceed to the next line of code when that signal has been called, or is there something I’m missing here? Thanks!

:bust_in_silhouette: Reply From: Rogelio Armegio

im new with godot using c# but as i’ve seen, to use await properly that procces has to be inside an async function, put all the specific procces of the await inside a function like this,

  private async void anynamefunc()
{
//procces  that is controlled with the await ToSignal
}

honestly i didn’t analyze your code so much but i see to await to signal so, i would use two asyc functions, one for any case you use await, then just call those functions where you removed the code, i would do it like this…

while (isTurnManagerRunning)
    {
        for (int i = 0; i < turns.Count; i++)
        {
            currentTurn = turns[i];
            if (turns[i] is Player)
            {
                GD.Print("-----------It's the Player's turn");

                if (player == null)
                    player = turns[i] as Player;

                anynamefunc1();

            }
            else if (turns[i] is Enemy)
            {
                GD.Print("-----------It's the Enemy's turn");
                enemy = turns[i] as Enemy;
                enemy.RunAI();
                enemy = null;

               anynamefunc2();

            }
        }
    }

private async void anynamefunc1()
{
                player.StartTurn();
                await ToSignal(player, "turn_completed");
               anynamefunc2();
}

private async void anynamefunc2()
{
                GD.Print("Starting turn timer");
                timer.Start();
                await ToSignal(timer, "timeout");
                GD.Print("The Enemy's turn has finished");
}

also i dont have a very good english, since it isn’t my native language and probably that was not the correct place to put those functions but you will now where to put them