Важно отметить, что я НЕ удаляю данные приложения на устройстве. Я ожидаю, что если пользователь ранее играл онлайн, его данные игрока для player/{userId} будут загружены и кэшированы локально благодаря KeepSynced(true) и, таким образом, будут немедленно доступны при запуске в автономном режиме.
Сценарий:
- Пользователь играет онлайн: пользователь успешно входит в систему (анонимно или ранее зарегистрированный), и его данные игрока (например, монеты, рекорды) создается/обновляется на сервере Firebase RTDB под именем player/{userId} . Этот путь активно синхронизируется с userRef.KeepSynced(true), и прослушиватель ValueChanged подключается в режиме онлайн. Мой локальный PlayerDataCache обновляется, и сценарий CloudSync передает изменения на сервер.
- Пользователь закрывает приложение.
- Пользователь запускает приложение полностью в автономном режиме: устройство отключено от Интернета (Wi-Fi и мобильная передача данных отключены). Данные приложения НЕ удаляются; локальный кеш Firebase должен сохраниться.
- Инициализация приложения: запускается мой скрипт FirebaseInitLoader:
- Он успешно идентифицирует постоянного пользователя (auth.CurrentUser не имеет значения NULL, установлен ActiveUserId).
- Он присоединяет ValueChanged прослушиватель пути player/{userId} и вызывает userRef.KeepSynced(true).
После сообщения журнала «FirebaseInitLoader: ValueChanged-Listener für Spielerdaten angehängt.», обратный вызов OnPlayerDataChanged никогда не срабатывает, когда приложение запускается в автономном режиме в этом сценарий. Следовательно, флаг _initialLoadHandled остается false , и playerDataLoaded никогда не устанавливается в true самим прослушивателем.
Мои ожидания:
При запуске в автономном режиме и с учетом того, что:
SetPersistenceEnabled(true) устанавливается раньше.
userRef.KeepSynced(true) вызывается для соответствующего пути.
Приложение ранее было в сети и взаимодействовал с этими данными, поэтому они должны находиться в локальном кеше.
Локальные данные приложения НЕ были очищены.
...прослушиватель ValueChanged должен немедленно сработать с локально кэшированными данными. Затем приложение должно иметь возможность загружать фактический прогресс пользователя, а не резервные данные.
Основная проблема: несмотря на все конфигурации для сохранения в автономном режиме и предварительного взаимодействия с данными в Интернете, прослушиватель ValueChanged, похоже, не обращается к существующим локально кэшированным данным или не запускается ими при запуске в автономном режиме.
Фрагменты кода
Код: Выделить всё
using UnityEngine;
using Firebase.Auth;
using Firebase.Database;
using Firebase.Extensions;
using System;
using System.Collections;
using System.Collections.Generic;
public class FirebaseInitLoader : MonoBehaviour
{
private FirebaseAuth auth;
private DatabaseReference dbRef;
public bool playerDataLoaded = false;
private bool _initialLoadHandled = false;
private Coroutine _initialLoadTimeoutCoroutine;
private bool _listenerFiredInitially = false;
public static string ActiveUserId { get; private set; }
void Start()
{
Debug.Log("FirebaseInitLoader: Start wird ausgeführt.");
#if UNITY_EDITOR
FirebaseDatabase.DefaultInstance.SetPersistenceEnabled(false);
#else
FirebaseDatabase.DefaultInstance.SetPersistenceEnabled(true); // Enabled on device
#endif
auth = FirebaseAuth.DefaultInstance;
dbRef = FirebaseDatabase.DefaultInstance.RootReference;
if (auth.CurrentUser != null)
{
ActiveUserId = auth.CurrentUser.UserId;
Debug.Log($"FirebaseInitLoader: Persistierter User angemeldet: {ActiveUserId}.");
SetupPlayerDatabaseListener(ActiveUserId);
return;
}
if (Application.internetReachability == NetworkReachability.NotReachable)
{
Debug.LogWarning("FirebaseInitLoader: Erster Start offline: noch kein Firebase-User. Verwende In-Memory-Defaults.");
// Fallback for no user offline
PlayerDataCache.InitNewPlayer("Player_Offline");
playerDataLoaded = true;
_initialLoadHandled = true;
return;
}
Debug.Log("FirebaseInitLoader: Kein gültiger User gefunden. Melde anonym an.");
auth.SignInAnonymouslyAsync().ContinueWithOnMainThread(task =>
{
if (task.IsCompletedSuccessfully && task.Result != null)
{
ActiveUserId = auth.CurrentUser.UserId;
Debug.Log($"FirebaseInitLoader: Anonyme Anmeldung erfolgreich: {ActiveUserId}.");
SetupPlayerDatabaseListener(ActiveUserId);
}
else
{
Debug.LogError($"FirebaseInitLoader: Anonyme Anmeldung fehlgeschlagen: {task.Exception}.");
// Fallback for failed anonymous login
PlayerDataCache.InitNewPlayer("Player_Offline_LoginFailed");
playerDataLoaded = true;
_initialLoadHandled = true;
}
});
}
void SetupPlayerDatabaseListener(string uid)
{
Debug.Log($"FirebaseInitLoader: SetupPlayerDatabaseListener für UID: {uid}.");
var userRef = dbRef.Child("players").Child(uid);
userRef.KeepSynced(true); // Keep data for this path synced
_listenerFiredInitially = false;
userRef.ValueChanged += OnPlayerDataChanged;
Debug.Log("FirebaseInitLoader: ValueChanged-Listener für Spielerdaten angehängt.");
if (!_initialLoadHandled)
{
_initialLoadTimeoutCoroutine = StartCoroutine(InitialLoadTimeout(uid));
Debug.Log("FirebaseInitLoader: Initial Load Timeout Coroutine gestartet.");
}
}
IEnumerator InitialLoadTimeout(string uid)
{
Debug.Log($"FirebaseInitLoader: InitialLoadTimeout Coroutine gestartet für UID: {uid}. Warte 5 Sekunden...");
yield return new WaitForSeconds(5f);
Debug.Log($"FirebaseInitLoader: InitialLoadTimeout Coroutine beendet Wartezeit für UID: {uid}.");
if (!_listenerFiredInitially && !_initialLoadHandled)
{
Debug.LogWarning($"FirebaseInitLoader: Timeout (5s) abgelaufen für Initial Load. " +
$"OnPlayerDataChanged hat noch nicht gefeuert für UID: {uid}. " +
$"Aktueller Netzwerkstatus: {Application.internetReachability}");
if (!playerDataLoaded)
{
PlayerDataCache.InitNewPlayer("Player_Offline_Timeout");
playerDataLoaded = true;
_initialLoadHandled = true;
Debug.Log("FirebaseInitLoader: Initialisierung abgeschlossen (Timeout-Fallback). playerDataLoaded = true.");
}
}
_initialLoadTimeoutCoroutine = null;
}
void OnPlayerDataChanged(object sender, ValueChangedEventArgs e)
{
Debug.Log($"FirebaseInitLoader: OnPlayerDataChanged aufgerufen. _initialLoadHandled: {_initialLoadHandled}. ListenerFiredInitially: {_listenerFiredInitially}");
_listenerFiredInitially = true;
if (_initialLoadTimeoutCoroutine != null)
{
StopCoroutine(_initialLoadTimeoutCoroutine);
_initialLoadTimeoutCoroutine = null;
Debug.Log("FirebaseInitLoader: Initial Load Timeout Coroutine gestoppt, da OnPlayerDataChanged gefeuert hat.");
}
if (e.DatabaseError != null)
{
Debug.LogError("FirebaseInitLoader: Fehler bei Spielerdaten über Listener: " + e.DatabaseError.Message);
if (!_initialLoadHandled) { // Fallback in case of listener error
PlayerDataCache.InitNewPlayer("Player_FallbackError_Listener");
playerDataLoaded = true;
_initialLoadHandled = true;
}
return;
}
if (!_initialLoadHandled) // Process initial load
{
_initialLoadHandled = true;
if (e.Snapshot != null && e.Snapshot.Exists)
{
Debug.Log("FirebaseInitLoader: Spielerdaten existieren (über Listener-Snapshot).");
PlayerDataCache.LoadFromSnapshot(e.Snapshot);
playerDataLoaded = true;
}
else // No data in snapshot (even after first firing)
{
Debug.Log("FirebaseInitLoader: Keine Spielerdaten im initialen Snapshot gefunden.");
if (Application.internetReachability != NetworkReachability.NotReachable)
{
Debug.Log("FirebaseInitLoader: Online-Verbindung erkannt. Erstelle neue Spielerdaten.");
// CreateNewPlayerData will set playerDataLoaded = true
CreateNewPlayerData(ActiveUserId);
}
else
{
Debug.LogWarning("FirebaseInitLoader: Keine Spielerdaten gefunden und offline. Verwende In-Memory-Defaults.");
PlayerDataCache.InitNewPlayer("Player_Offline_NoData_Listener");
playerDataLoaded = true;
}
}
}
else // Subsequent updates
{
if (e.Snapshot != null && e.Snapshot.Exists)
{
PlayerDataCache.LoadFromSnapshot(e.Snapshot);
}
}
}
// CreateNewPlayerData, OnDestroy and PlayerDataCache are not included for brevity,
// but PlayerDataCache is a static class for in-memory game state.
}
Код: Выделить всё
// Simplified for brevity
public class CloudSync : MonoBehaviour
{
DatabaseReference dbRef;
// ... other fields and methods
void Start()
{
dbRef = FirebaseDatabase.DefaultInstance.RootReference;
}
public void Flush(bool forceFullSnapshot = false)
{
if (dbRef == null || string.IsNullOrEmpty(PlayerDataCache.UserId)) return;
var root = $"players/{PlayerDataCache.UserId}";
var updates = new Dictionary();
// ... populate updates dictionary based on 'dirty' flags
// Example: updates[$"{root}/dragCoins"] = PlayerDataCache.DragCoins;
dbRef.UpdateChildrenAsync(updates).ContinueWithOnMainThread(t =>
{
if (t.IsFaulted || t.IsCanceled)
{
Debug.LogWarning("CloudSync Flush failed → retry: " + t.Exception);
// Mark as dirty again to retry
}
});
}
}
Почему прослушиватель ValueChanged не срабатывает с локально кэшированными данными при запуске в автономном режиме, хотя используются SetPersistenceEnabled(true) и KeepSynced(true), а приложение ранее загружало и взаимодействовало с этими данными в режиме онлайн без очистки данных приложения? Что может помешать слушателю получить доступ к локальному кешу сразу после автономного запуска? Как обеспечить, чтобы прослушиватели ValueChanged всегда запускались с доступными кэшированными данными в автономном режиме?
Дополнительный контекст:
Версия Unity: [Deine Unity-Version]
Версия Firebase Unity SDK: [Deine Firebase SDK-Version, z.B. 11.6.0]
Целевая платформа: Android
Application.internetReachability правильно сообщает NotReachable в автономном режиме.
Аутентификация Firebase успешно сохраняет идентификатор пользователя.
Мой вопрос:
Почему прослушиватель ValueChanged не срабатывает с локально кэшированными данными при запуске в автономном режиме, хотя SetPersistenceEnabled(true) и KeepSynced(true) используются, и приложение ранее загружало эти данные и взаимодействовало с ними в режиме онлайн без очистки данных приложения? Что может помешать слушателю получить доступ к локальному кешу сразу после автономного запуска? Как обеспечить, чтобы прослушиватели ValueChanged всегда запускались с доступными кэшированными данными в автономном режиме?
Дополнительный контекст:
Версия Unity: 6000.2.6f2
Версия Firebase Unity SDK: 12.5.0
Целевая платформа: Android
Application.internetReachability правильно сообщает NotReachable в автономном режиме.
Аутентификация Firebase успешно сохраняет идентификатор пользователя.
Подробнее здесь: https://stackoverflow.com/questions/797 ... -with-exis
Мобильная версия