Home > Back-end >  Why is part of my code not executed if I enable/disable Unity gameObject?
Why is part of my code not executed if I enable/disable Unity gameObject?

Time:08-05

I was using PlayFab and decided to migrate to Firebase. I want to use FirebaseAuth to implement a login system in my Unity game (and removed the PlayFab code).

So I made a FirebaseManager.cs class, and modified the Script Execution Order to make it run before any other custom script.

I tried to implement what is necessary to create an account and it works great. I know Firebase silently keeps the user connected between sessions. So I want to check wether a user is already connected or not, from my "home scene" (which is also the scene where you login/create account).

public class FirebaseManager : MonoBehaviour
{
    private static FirebaseManager _instance;
    public static FirebaseManager Instance { ... }

    private FirebaseApp _app;

    private FirebaseAuth _auth;
    public FirebaseAuth Auth
    {
        get
        {
            if (_auth == null)
            {
                _auth = FirebaseAuth.GetAuth(_app);
            }
            return _auth;
        }
    }

    private FirebaseUser _user;
    public FirebaseUser User
    {
        get
        {
            return _user;
        }
        set
        {
            _user = value;
        }
    }

    // Events to subscribe to for this service
    public delegate void FirebaseInitialized();
    public static event FirebaseInitialized OnFirebaseInitialized;
    
    private void Awake()
    {
        if (_instance == null)
        {
            DontDestroyOnLoad(this.gameObject);
            _instance = this;
            InitializeFirebase();
        }
        else { Destroy(this.gameObject); }
    }

    private void InitializeFirebase()
    {
        Debug.Log($"FirebaseManager.cs > INITIALIZING FIREBASE");
        Firebase.FirebaseApp.CheckAndFixDependenciesAsync().ContinueWith(task =>
        {
            var dependencyStatus = task.Result;
            if (dependencyStatus == Firebase.DependencyStatus.Available)
            {
                // Create and hold a reference to your FirebaseApp,
                // where app is a Firebase.FirebaseApp property of your application class.
                _app = FirebaseApp.DefaultInstance;

                // Create and hold a reference to FirebaseAuth
                _auth = FirebaseAuth.DefaultInstance;

                // Create and hold a reference to already logged in user (if any)
                _user = _auth.CurrentUser;

                if (OnFirebaseInitialized != null) OnFirebaseInitialized.Invoke();
            }
            else
            {
                UnityEngine.Debug.LogError(System.String.Format(
                  "Could not resolve all Firebase dependencies: {0}", dependencyStatus));
                // Firebase Unity SDK is not safe to use here.
            }
        });
    }

    private void OnDestroy()
    {
        if (_auth != null) _auth = null;
    }
}

I also made a static AuthService.cs class to call from my scene :

public static class AuthService
{
    private static FirebaseAuth auth = FirebaseManager.Instance.Auth;
    
    public static bool IsUserAlreadyLoggedIn()
    {
        Debug.Log($"AuthService.cs > Checking if user already logged in");
        return auth.CurrentUser != null;
    }
}

Then in my Login Scene, I have this LoginManager.cs class :

public class LoginManager : MonoBehaviour
{
    // Loading Animation
    [Header("Loading Animation")]
    public GameObject loadingAnimation;

    private void Awake()
    {
        FirebaseManager.OnFirebaseInitialized  = OnFirebaseInitialized;
    }

    private void Start()
    {
        ShowLoadingAnimation(true);
    }

    private void OnFirebaseInitialized()
    {
        if (AuthService.IsUserAlreadyLoggedIn())
        {
            Debug.Log($"Already LoggedIn");
            OnLoginSuccess();
        }
        else
        {
            Debug.Log($"No User LoggedIn");
            ShowLoadingAnimation(false);
        }
    }

    private void OnLoginSuccess()
    {
        ShowLoadingAnimation(false); // From here, no code is executed
        Debug.Log($"Logged In as: {FirebaseManager.Instance.Auth.CurrentUser.Email}");
        // SceneLoader.Instance.Load(SceneLoader.Scene.CharacterCreation);
    }

    private void OnDestroy()
    {
        FirebaseManager.OnFirebaseInitialized -= OnFirebaseInitialized;
    }

    /// <summary>
    /// Show or Hide the Loading Animation
    /// </summary>
    /// <param name="show">True = show || False = hide</param>
    private void ShowLoadingAnimation(bool show)
    {
        loadingAnimation.SetActive(show);
    }
}

The Loading animation gameObject is just an animated image ( another image which is used to block user input).

Whenever the code reaches the ShowLoadingAnimation() method. All code after it is NOT executed. But if I remove all the calls to ShowLoadingAnimation(), everything works fine. The same logic was working fine with PlayFab.

I just don't understand what is happening. Maybe something to do with threads? Or maybe just my Firebase implementation is wrong? What can I do?

Thanks.

CodePudding user response:

One explanation would be that OnFirebaseInitialized gets called on a background thread.

Unity's MonoBehaviour system isn't thread safe, so if a GameObject is set inactive from a background thread this could result in exceptions and in code execution coming to a stop.

To avoid the issue you could have OnLoginSuccess just set a boolean variable to true, and then inside the Update method (which gets called on the main thread) check when this variable is true and call ShowLoadingAnimation(false).

You should also only access this boolean variable inside a lock statement to ensure thread safety.

CodePudding user response:

So I've found a solution. As Sisus said (thanks for your answer, it helped me in my Google searches), this issue is caused by the code running on another thread instead of the main one.

According to this Firebase video, when initializing Firebase, I can call ContinueWithOnMainThread instead of simply ContinueWith to solve that issue.

(Note that this require using Firebase.Extensions, see Firebase.Extensions.TaskExtension docs)

So updated Firebase initialization looks like this:

Firebase.FirebaseApp.CheckAndFixDependenciesAsync().ContinueWithOnMainThread(task =>
{
    var dependencyStatus = task.Result;
    if (dependencyStatus == Firebase.DependencyStatus.Available)
    {
        // Create and hold a reference to your FirebaseApp,
        // where app is a Firebase.FirebaseApp property of your application class.
        _app = FirebaseApp.DefaultInstance;

        // Create and hold a reference to FirebaseAuth
        _auth = FirebaseAuth.DefaultInstance;

        // Create and hold a reference to already logged in user (if any)
        _user = _auth.CurrentUser;

        if (OnFirebaseInitialized != null) OnFirebaseInitialized.Invoke();
  }
  else
  {
      UnityEngine.Debug.LogError(System.String.Format(
      "Could not resolve all Firebase dependencies: {0}", dependencyStatus));
      // Firebase Unity SDK is not safe to use here.
  }

});

  • Related