Search Unity

Question How to wait for a database result?

Discussion in 'Getting Started' started by XoetziX, Jan 30, 2022.

  1. XoetziX

    XoetziX

    Joined:
    Mar 16, 2021
    Posts:
    16
    Hello everyone,

    I guess I have a rather standard issue, but I do not figure out how to solve it.

    The szenario is a racing game where at the moment the finish line is reached the following things shall happen:
    1. Load previous highscores from the database
    2. Do some magical calculation (check if new top highscore, etc.)
    3. Save the new highscore list to the database
    The problem I am facing right now is that 2 and 3 is done before I got the result from 1, hence I do not have the stored highscores yet before the rest is done.

    So at the end I "simply" looking for a way to wait until loading the database is finished.

    I tried different approaches with Coroutines and "yield return new WaitUntil" but non of it worked.

    Here are the relevant parts of my code:

    Class: HighscoreController
    Code (CSharp):
    1.  
    2. public void AddHighScore(string playerName, float time)
    3.      {
    4.          currentPlayerHighscore = new PlayerHighscore(playerName, time);
    5.          //LoadHighscores();
    6.          highscoresDB = FirebaseManagerGame.instance.LoadHighscoresOfLevel(levelInfo.LevelName);
    7.          Debug.LogWarning("HERE I HAVE TO WAIT UNTIL DATABASE RESULT IS THERE");
    8.          //reset values
    9.          newTopHighscore = false; inTop10 = false; notInTop10 = false;
    10.         [...] some magical calculation [...]
    11.          highscoresDB.Sort(SortByTime);
    12.    
    13.          SaveHighscoresDB();
    14.          ShowHighScores();
    15.      }

    Class: FirebaseManagerGame

    Code (CSharp):
    1.  
    2.  
    3. public List<PlayerHighscore> LoadHighscoresOfLevel(string levelName)
    4.      {
    5.          Debug.Log("FireBaseGame - LoadHighscoresOfLevel START");
    6.          List<PlayerHighscore> tmpList = new List<PlayerHighscore>();
    7.          StartCoroutine(GetHighscoresOfLevel(levelName, returnValue =>
    8.          {
    9.              tmpList = returnValue;
    10.          }));
    11.          Debug.Log("LoadHighscoresOfLevel List.count: " + tmpList.Count);
    12.          return tmpList;
    13.      }
    14.  
    15.  
    16.      private IEnumerator GetHighscoresOfLevel(string levelName, System.Action<List<PlayerHighscore>> callback)
    17.      {
    18.          Debug.Log("FireBaseGame - getHighscoresOfLevel START");  
    19.          List<PlayerHighscore> tmpHighscores = new List<PlayerHighscore>();          
    20.          Task<DataSnapshot> DBTask = DBreference.Child("levelHighscores").Child(levelName).GetValueAsync();
    21.          yield return new WaitUntil(predicate: () => DBTask.IsCompleted);
    22.          if (DBTask.Exception != null)
    23.          {
    24.              Debug.LogWarning(message: $"Failed to register task with {DBTask.Exception}");
    25.          }
    26.          else if (DBTask.Result.Value == null) //No data exists yet
    27.          {
    28.              Debug.Log("getHighscoresOfLevel - NO DATA EXISTS");
    29.          }
    30.          else //Data has been retrieved
    31.          {
    32.              DataSnapshot snapshot = DBTask.Result;
    33.              tmpHighscores = JsonConvert.DeserializeObject<List<PlayerHighscore>>(snapshot.GetRawJsonValue());
    34.              Debug.Log("getHighscoresOfLevel - FOUND DATA - count: " + tmpHighscores.Count);
    35.              callback(tmpHighscores);
    36.          }
    37.      }
    I hope you can help me to solve this issue.

    Thanks in advance!

    oetzi
     
  2. XoetziX

    XoetziX

    Joined:
    Mar 16, 2021
    Posts:
    16
    Here is the solution. The magic trick are 2 words: "yield return" which cause the method to wait until the database results returned. To be able to use these magic words I had to change the AddHighScore method into an IEnumerator.

    Code (CSharp):
    1. public IEnumerator AddHighScore(string playerName, float time)
    2. {
    3.      currentPlayerHighscore = new PlayerHighscore(playerName, time);
    4.      //IEnumerator cannot return any internal values / variables. Hence, the following way can be used:
    5.      //pass a place holder (= returnValue) to the IEnumerator. Within the IEnumerator this placeholder can be set to any value. Outside the Enumerator / here it can be further processed.
    6.      yield return StartCoroutine(FirebaseManagerGame.instance.GetHighscoresOfLevel(levelInfo.LevelName, returnValue =>
    7.      {
    8.          highscoresDB = returnValue;
    9.      }));
    10. [...]