I need to take the value of a boolean variable, but I can't specify that the local variable is a boolean. How can I do that?
var value = dbRef.Child("Values").Child("updateIsReady").GetValueAsync();
if(value)
{
//something is happening
}
Try to implement my application's update check through a variable in the database that changes when an update is released
You should get the value once its ready in task.IsCompleted:
FirebaseDatabase.DefaultInstance.GetReference("Values")
.GetValueAsync().ContinueWithOnMainThread(task => {
if (task.IsFaulted) {
// Handle the error...
}
else if (task.IsCompleted) {
//get the data here:
//this is snapshot of the reference
DataSnapshot snapshot = task.Result;
//this is your boolean, you get it from the snapshot
bool updateIsReady = snapshot.Child("updateIsReady").Value;
}
});
Related
Whenever I reopen the game and start scoring again it replaces the previous data. Please help me with this and tell me where I'm doin it wrong.
I'm using Firebase Realtime Database.
This is my score script down below:
public static int cashValue;
Text cash;
void Start()
{
cash = GetComponent();
}
void Update()
{
cash.text = "" + cashValue;
}
This is where I save the score in Firebase Realtime Database:
private IEnumerator UpdateKills(int _kills)
{
//Set the currently logged in user kills
var DBTask = DBreference.Child("users").Child(User.UserId).Child("kills").SetValueAsync(_kills);
yield return new WaitUntil(predicate: () => DBTask.IsCompleted);
if (DBTask.Exception != null)
{
Debug.LogWarning(message: $"Failed to register task with {DBTask.Exception}");
}
else
{
//Kills are now updated
}
}
And This is Where I Retrieve the data from Firebase Realtime Database:
private IEnumerator LoadUserData()
{
//Get the currently logged in user data
var DBTask = DBreference.Child("users").Child(User.UserId).GetValueAsync();
yield return new WaitUntil(predicate: () => DBTask.IsCompleted);
if (DBTask.Exception != null)
{
Debug.LogWarning(message: $"Failed to register task with {DBTask.Exception}");
}
else if (DBTask.Result.Value == null)
{
//No data exists yet
xpField.text = "0";
killsField.text = "0";
deathsField.text = "0";
}
else
{
//Data has been retrieved
DataSnapshot snapshot = DBTask.Result;
xpField.text = snapshot.Child("xp").Value.ToString();
killsField.text = snapshot.Child("kills").Value.ToString();
deathsField.text = snapshot.Child("deaths").Value.ToString();
}
}
It works perfectly, but the only problem is that it replacing the Score instead of updating it or start from where I left.
The flow should go like this:
int myData;
bool isMyDataLoaded = false;
void Start() {
LoadMyData();
}
void LoadMyData() {
StartCoroutine(IELoadMyData());
}
void SetMyData() {
if(isMyDataLoaded) {
// Set only if the data from database was retreived
StartCoroutine(IESetMyData());
}
}
IEnumerator IELoadMyData() {
// Load your data from database here
if(<fetch was successful>) {
myData = <the result>;
isMyDataLoaded = true; // Set data loaded as true
}
}
IEnumerator IESetMyData() {
// Update your data here
}
Now whenever you want to update, Call SetMyData. It will only set the new data in the DB if the data was first fetched in the local game successfully.
Something to watch out for is that you might be falling victim to caching. Basically, GetValueAsync typically returns whatever's cached locally (unless you disable persistence -- which you probably shouldn't) and asks the server for an update. SetValueAsync writes to the local cache and eventually syncs it up in the background.
Therefore, to update a value, you should always use a Transaction. From https://firebase.google.com/docs/database/unity/save-data#save_data_as_transactions
private void UpdateKills(int _kills) {
DBreference.Child("users").Child(User.UserId).Child("kills").RunTransaction(mutableData => {
// if the data isn't an int or is null, just make it 0
// then add the new number of kills
var kills = ((mutableData.value as? int) ?? 0) + _kills;
return TransactionResult.Success(mutableData);
});
}
This would require reworking your logic so that rather than saving the total kills, you record the delta. Then this transaction would run repeatedly until it succeeds (usually once, unless your data is out of sync in which case usually twice).
For getting data, you're best off registering ValueChanged listeners and updating your UI directly from those. This way you're always in sync with your server, and it's smart enough to use a local cache so you don't have to worry about always blocking on a network access (you'll get a null if there's nothing cached). Also, as the local cache updates in the Transaction, you will get ValueChanged events with the latest data.
Obviously, not every game can be stable around randomly receiving value updates from a server. But if you can move your data to be more reactive around Realtime Database, you'll make the best use of its inbuilt local caching and sync logic.
This is my first time using Firebase with Unity. My code seems to be connected to my Firestore database but is not retrieving the data.
I also get this error message: System.Threading.Tasks.ContinuationResultTaskFromResultTask2[Firebase.Firestore.DocumentSnapshotProxy,Firebase.Firestore.DocumentSnapshot] UnityEngine.Debug:Log(Object) <>c:<savedata>b__5_0(Task1) (at Assets/Scripts/CloudFirebase.cs:60)
The corresponding line for CloudFirebase.cs:60 is Debug.Log(task); and the code normally gets stuck on is line DocumentSnapshot snapshot = task.Result;
System.Threading._ThreadPoolWaitCallback:PerformWaitCallback()
Note: I have tried to look around for answers relating to my issue but can't seem to find any. If you have any links that relate specifically to Unity please do share.
Here's my code below:
FirebaseFirestore db;
Dictionary<string, object> user;
CollectionReference stuRef;
private bool istrue = true;
// Start is called before the first frame update
void Start()
{
Debug.Log("Start called");
Firebase.FirebaseApp.CheckAndFixDependenciesAsync().ContinueWith(task => {
var dependencyStatus = task.Result;
if (dependencyStatus == Firebase.DependencyStatus.Available)
{
Debug.Log("available for use");
// Create and hold a reference to your FirebaseApp,
// where app is a Firebase.FirebaseApp property of your application class.
//app = Firebase.FirebaseApp.DefaultInstance;
db = FirebaseFirestore.DefaultInstance;
Debug.Log(db.Collection("Users").Document("hello"));
stuRef = db.Collection("Users");
savedata();
// Set a flag here to indicate whether Firebase is ready to use by your app.
}
else
{
UnityEngine.Debug.LogError(System.String.Format(
"Could not resolve all Firebase dependencies: {0}", dependencyStatus));
// Firebase Unity SDK is not safe to use here.
}
});
//db = FirebaseFirestore.DefaultInstance;
//if (istrue)
//{
// savedata();
// istrue = false;
//}
}
public void savedata()
{
stuRef.Document("hello").GetSnapshotAsync().ContinueWith(task =>
{
if (task.IsCompleted)
{
task.GetAwaiter();
if (task.IsFaulted)
{
Debug.Log(task.Exception.Message);
}
Debug.Log("succesfully added to database");
Debug.Log(task);
DocumentSnapshot snapshot = task.Result;
if (snapshot.Exists)
{
Debug.Log("snapshot exists");
//user = snapshot.ToDictionary();
// foreach (KeyValuePair<string, object> pair in user)
// {
// Debug.Log(("{0}:{1}", pair.Key, pair.Value));
// }
}
else
{
Debug.Log("snapshot does not exist");
}
}
else
{
Debug.Log("failed db");
}
});
}
A few notes, which should help you debug:
Exceptions in Task are of type AggregateException. This means that rather than printing out the exception itself with Debug.Log(task.Exception.Message);, you should iterate over the exceptions it contains (usually only 1 in the case of Firebase) and print out each internal exception.
I tend to log these using the Flatten method. Paraphrasing Microsoft's documentation:
foreach (var e in task.Exception.Flatten().InnerExceptions) {
Debug.LogWarning($"Received Exception: {e.Message}");
}
You could also use Handle. Again paraphrasing the official docs:
task.Exception.Handle(e => {
Debug.LogWarning($"Received Exception: {e.Message}");
});
I suspect that the error will become apparent once you change your code accordingly. But feel free to update the bug with the appropriate exception text if you remain stuck.
Additionally:
You shouldn't have to call Task.IsCompleted, this will always be true inside a continuation (see the docs).
You shouldn't need to call Task.GetAwaiter, this is intended for the compiler's use (although don't let me stop you if you're doing something creative).
You should replace ContinueWith with ContinueWithOnMainThread, this is a Firebase-provided extension method that ensures that continuations run on the main (Unity) thread. You'll immediately run into new and exciting exceptions if you touch almost anything in the UnityEngine namespace in your continuation.
My suspicion would be that you have an issue with security rules, but that will become more apparent once you fix your error logging.
I would make sure that the snapshot exist as in the docs:
DocumentReference docRef = db.Collection("Users").Document("hello");
DocumentSnapshot snapshot = await docRef.GetSnapshotAsync();
if (snapshot.Exists)
{
//whatever
} else
{
Console.WriteLine("Document {0} does not exist!", snapshot.Id);
}
So that the existance of the doc can be discarded as a problem.
I would also tell in the question what is the corresponding line CloudFirebase.cs:60 in your code where you obtain the error and if its not from your code, try to provide the last line called from your code where the error is produced.
I am using the following code to retrieve data from Firebase database on a user with Unity3D, in our case i am getting User Level:
FirebaseDatabase.DefaultInstance
.GetReference("users").Child(userID)
.GetValueAsync().ContinueWith(task =>
{
if (task.IsFaulted)
{
Debug.LogError("Error retriving user data: " + userID);
// Handle the error...
}
else if (task.IsCompleted)
{
DataSnapshot snapshot = task.Result;
int TempUserLevel = (int)snapshot.Child("Level").Value;
//this get's an error
PlayerPrefs.SetInt(_UserLevel, TempUserLevel);
}
}
Error:
TrySetInt can only be called from the main thread. Constructors and
field initializers will be executed from the loading thread when
loading a scene.
As I understand the TASK is a new thread and not Unity Main thread. Still
I can't seem save values locally on unity, or get the value out of the TASK.
It cannot be called because continue with is a delegate and it waits for response. What I did is just made a waituntil coroutine before calling this delegate using and set a bool for instance some bool check = false.
else if(task.IsCompleted)
{
// your operation
check=true;
}
////////
IEnumerator myRoutine()
{
yield return new WaitUntil ( () => check );
// set your playerprefs.
}
Actually you could just change "ContinueWith" to "ContinueWithOnMainThread".
You can change ContinueWith to ContinueWithOnMainThread, but you will need add "using Firebase.Extensions;"
using Firebase.Extensions;
FirebaseDatabase.DefaultInstance.GetReference("users").Child(userID)
.GetValueAsync().ContinueWithOnMainThread(task =>
{
if (task.IsFaulted)
{
// Handle the error...
}
else if (task.IsCompleted)
{
DataSnapshot snapshot = task.Result;
// Do something with snapshot...
}
});
See documentation for more examples: https://firebase.google.com/docs/database/unity/retrieve-data
Potential Note: If you are following documentation for Google Sign in with Firebase and downloaded the Firebase SDK for Unity 2020, you may get an error regarding a conflict with System.Threading.Tasks (this happened to me). If anyone else gets this error, it can be dealt with by deleting or renaming the Unity.Compat and Unity.Tasks files under Unity > Assets > Parse > Plugins, but do not change or delete the files in the dotNet45 folder.
I've been searching a lot for this question throughout the last week, but all I could ever find was an answer to the same question but for the android firebase API, which is different from the Unity one.
Here's the code that I'm trying to use to see if a username/value is already in the database:
public void CheckIfUsernameExists(string nick)
{
System.Threading.Tasks.Task t = db.Child("userinfo").Child(nick).GetValueAsync().ContinueWith(task =>
{
if (task.IsFaulted)
{
Debug.LogError(task.Result.ToString() + "faulted");
usernameExists = false;
usernameNotExistsEvent.Invoke();
}
else if (task.IsCompleted)
{
DataSnapshot snapshot = task.Result;
if (snapshot != null)
{
usernameExists = true;
Debug.LogError(task.Result.ToString() + "completed");
usernameExistsEvent.Invoke();
}
else
{
Debug.LogError(task.Result.ToString() + "elsecompleted");
usernameNotExistsEvent.Invoke();
}
}
else if (task.IsCanceled)
{
Debug.LogError(task.Result.ToString() + "canceled");
usernameNotExistsEvent.Invoke();
}
});
}
Where usernameExistsEvent and usernameNotExistsEvent are UnityEvent objects to which I add listeners to do things depending on what the result was.
I'm making a simple online scoreboard, but I don't want two people to have the same username on the scoreboard for logistics purposes, so I'm checking if the username exists prior to letting the user take it.
Here are the rules that I have for that path up on Firebase:
{
"rules": {
"scores": {
".read": "data.child(auth.uid).exists()",
"$user_id": {
".write":"$user_id === auth.uid"
}
},
"userinfo": {
"$user_id": {
".write":"!data.exists()",
".read": "true"
}
}
}
}
For some odd reason, even when the username does not exist, the task is marked as completed, and thus my code doesn't really work.
Any ideas on how to do this easily with Firebase?
Thank you in advance.
I actually found a simple solution. The problem was that whether or not the data actually exists on the database, since you're passing it a key for searching, snapshot will never be null.
There's a good piece of data inside the snapshot object though: the .Exists. That will tell you if the query actually was in the database or not.
I am using the following code to update data using the MongoDB C# Driver:
public async Task<bool> UpdateFirstName(string id, string firstName)
{
await Collection.UpdateOneAsync(Builders<User>.Filter.Eq(
"_id", new ObjectId(id)),
Builders<User>.Update.Set("firstName", firstName)
.CurrentDate("lastUpdatedDate"));
}
This method returns "bool", because I want to know if the data has been updated successfully. This would be the pseudocode for checking if the data has been updated successfully:
if (data updated successfully)
{
return true;
}
else
{
return false;
}
Does anyone know how to write the code for checking if the data updated successfully? Thanks!
If the method was executed so the update was done, otherwise the method will throw an exception - In case of async it's important to not forget the await (since using async method without await can't ensure your application stay around long enough to retreive the exception)
UpdateOneAsync has a return value of UpdateResult?, which gives you access to the ModifiedCount. As you you UpdateOne a single greater 0 check is enough.
var response = await Collection.UpdateOneAsync(Builders<User>.Filter.Eq(
"_id", new ObjectId(id)),
Builders<User>.Update.Set("firstName", firstName)
.CurrentDate("lastUpdatedDate"));
if response.ModifiedCount > 0 {
// success
}
// failed
This will throw an Exception if the Update is not acknowledged.