Reputation: 207
So, I am using Firebase Realtime Database for fetching a user values. I first check using DataSnapShot if a certain user exists or not. And then, if it exists, I call SceneManager.LoadScene (index). All the logs are correctly shown in the console. But SceneManager.LoadScene method won't work.
Here is the code block:
void authWithFirebaseFacebook(string accessToken)
{
Firebase.Auth.Credential credential =
Firebase.Auth.FacebookAuthProvider.GetCredential(accessToken);
auth.SignInWithCredentialAsync(credential).ContinueWith(task => {
if (task.IsCanceled) {
return;
}
if (task.IsFaulted) {
return;
}
loginSuccesful();
});
}
void loginSuccesful()
{
// SOME CODE HERE THEN (ONLY ASSIGNMENTS, NO TASK OR ANYTHING)
SaveUser();
}
public async void SaveUser()
{
DataSnapshot snapShot = await playerExistsAlready();
if (snapShot.Exists)
{
Debug.Log("Continue Fetching the details for user");
Debug.Log("Values fetched for new player...:" + snapShot.GetValue(true));
Debug.Log("Convert JSON to Object");
string json = snapShot.GetValue(true).ToString();
PlayerData newPlayer = JsonUtility.FromJson<PlayerData>(json);
UserManager.instance.player = newPlayer;
Debug.Log("User Creaated!");
Debug.Log("New Values are:" + newPlayer.DisplayName + " .." + newPlayer.PhotoURL) ;
Debug.Log("Load Main Menu Scene Now...");
SceneManager.LoadScene(1);
}
else
{
saveNewUser();
}
}
async Task<DataSnapshot> playerExistsAlready()
{
DataSnapshot dataSnapShot = await _database.GetReference("users/" + UserManager.instance.currentUserID ).GetValueAsync();
return dataSnapShot;
}
The output: Continue Fetching the details for user
Values fetched for new player...: (player data here)
Convert JSON to Object
User Created!
New Values are : (values here)
Load Main Menu Scene Now...
Upvotes: 0
Views: 556
Reputation: 90739
The issue is that your code is running on a separate background thread. Your main thread will not "know" that anything was called by a thread. In general most of the Unity API can not be called from a thread .. and it makes no sense to do.
Firebase specifically for that provides an extension method ContinueWithOnMainThread
which does exactly that: Makes sure that the callback is executed in the Unity main thread.
void authWithFirebaseFacebook(string accessToken)
{
Firebase.Auth.Credential credential = Firebase.Auth.FacebookAuthProvider.GetCredential(accessToken);
auth.SignInWithCredentialAsync(credential).ContinueWithOnMainThread(task =>
{
if (task.IsCanceled || task.IsFaulted)
{
return;
}
loginSuccesful();
});
}
void loginSuccesful()
{
// SOME CODE HERE THEN (ONLY ASSIGNMENTS, NO TASK OR ANYTHING)
SaveUser();
}
public void SaveUser()
{
_database.GetReference("users/" + UserManager.instance.currentUserID ).GetValueAsync().ContinueWithOnMainThread(task =>
{
if (task.IsCanceled || task.IsFaulted)
{
return;
}
var snapShot = task.Result;
if (snapShot.Exists)
{
Debug.Log("Continue Fetching the details for user");
Debug.Log("Values fetched for new player...:" + snapShot.GetValue(true));
Debug.Log("Convert JSON to Object");
string json = snapShot.GetValue(true).ToString();
PlayerData newPlayer = JsonUtility.FromJson<PlayerData>(json);
UserManager.instance.player = newPlayer;
Debug.Log("User Creaated!");
Debug.Log("New Values are:" + newPlayer.DisplayName + " .." + newPlayer.PhotoURL) ;
Debug.Log("Load Main Menu Scene Now...");
SceneManager.LoadScene(1);
}
else
{
saveNewUser();
}
}
}
Alternatively an often used pattern is a so called "main thread dispatcher" (I know there is an asset for that but this one requires a lot of refactoring actually ;) ).
But you can easily create one yourself. Usually it looks somewhat like
public class YourClass : MonoBehaviour
{
private ConcurrentQueue<Action> mainThreadActions = new ConcurrentQueue<Action>();
private void Update()
{
while(mainThreadActions.Count > 0 && mainThreadActions.TryDequeue(out var action))
{
action?.Invoke();
}
}
}
so any thread can just add an action to be executed on the main thread via e.g.
void authWithFirebaseFacebook(string accessToken)
{
Firebase.Auth.Credential credential = Firebase.Auth.FacebookAuthProvider.GetCredential(accessToken);
auth.SignInWithCredentialAsync(credential).ContinueWith(task =>
{
if (task.IsCanceled || task.IsFaulted)
{
return;
}
// either as method call
mainThreadActions.Enqueue(loginSuccesful);
});
}
void loginSuccesful()
{
// SOME CODE HERE THEN (ONLY ASSIGNMENTS, NO TASK OR ANYTHING)
SaveUser();
}
public void SaveUser()
{
// or as lambda exopression
database.GetReference("users/" + UserManager.instance.currentUserID ).GetValueAsync().ContinueWith(task =>
{
mainThreadActions.Enqueue(() =>
{
if (task.IsCanceled || task.IsFaulted)
{
return;
}
var snapShot = task.Result;
if (snapShot.Exists)
{
Debug.Log("Continue Fetching the details for user");
Debug.Log("Values fetched for new player...:" + snapShot.GetValue(true));
Debug.Log("Convert JSON to Object");
string json = snapShot.GetValue(true).ToString();
PlayerData newPlayer = JsonUtility.FromJson<PlayerData>(json);
UserManager.instance.player = newPlayer;
Debug.Log("User Creaated!");
Debug.Log("New Values are:" + newPlayer.DisplayName + " .." + newPlayer.PhotoURL) ;
Debug.Log("Load Main Menu Scene Now...");
SceneManager.LoadScene(1);
}
else
{
saveNewUser();
}
})
});
}
Upvotes: 1
Reputation: 207
So the problem was the method was not being executed on Main Thread. I guess Unity Methods like SceneManager.Load() etc are only called on a Main Thread. So, I used a class named UnityMainThreadDispatcher
And then I wrote
IEnumerator loadMainMenuScene()
{
SceneManager.LoadScene(1);
yield return null;
}
And called
UnityMainThreadDispatcher.Instance().Enqueue(loadMainMenuScene());
instead of
SceneManager.LoadScene(1);
Upvotes: 0