Прослушиватель Firebase Realtime Database ValueChanged не срабатывает в автономном режиме с существующими кешированными C#

Место общения программистов C#
Ответить
Anonymous
 Прослушиватель Firebase Realtime Database ValueChanged не срабатывает в автономном режиме с существующими кешированными

Сообщение Anonymous »

Я разрабатываю игру для Android на Unity, которая использует базу данных Firebase Realtime Database (RTDB) для хранения данных игроков. Я стремлюсь к надежному опыту работы в автономном режиме. Я включил сохранение RTDB с помощью FirebaseDatabase.DefaultInstance.SetPersistenceEnabled(true); и использую прослушиватели ValueChanged для извлечения данных. Моя основная проблема возникает, когда уже прошедший аутентификацию пользователь (который ранее играл онлайн и имеет данные на сервере Firebase) запускает приложение, находясь полностью в автономном режиме.
Важно отметить, что я НЕ удаляю данные приложения на устройстве. Я ожидаю, что если пользователь ранее играл онлайн, его данные игрока для player/{userId будут загружены и кэшированы локально благодаря KeepSynced(true) и, таким образом, будут немедленно доступны при запуске в автономном режиме.
Сценарий:
  • Пользователь играет онлайн: пользователь успешно входит в систему (анонимно или ранее зарегистрированные), а их данные об игроках (например, монеты, рекорды) создаются/обновляются на сервере Firebase RTDB под именем player/{userId . Этот путь активно синхронизируется с userRef.KeepSynced(true), и прослушиватель ValueChanged подключается в режиме онлайн. Мой локальный PlayerDataCache обновляется, и сценарий CloudSync передает изменения на сервер.
  • Пользователь закрывает приложение.
  • Пользователь запускает приложение полностью в автономном режиме: устройство отключено от Интернета (Wi-Fi и мобильные данные отключены). Данные приложения НЕ удаляются; локальный кеш Firebase должен сохраниться.
  • Инициализация приложения: выполняется сценарий My FirebaseInitLoader:
  • Он успешно идентифицирует постоянного пользователя ( auth.CurrentUser не имеет значения NULL, установлен ActiveUserId).
  • Он присоединяет прослушиватель ValueChanged к пути player/{userId и вызывает userRef.KeepSynced(true).
Наблюдаемое поведение:

Журналы приложения:

"FirebaseInitLoader: ValueChanged-Listener for Spielerdaten angehängt."

Но тогда обратный вызов OnPlayerDataChanged никогда не срабатывает, когда приложение запускается в автономном режиме. Следовательно, флаг _initialLoadHandled остается ложным, а 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: 6000.2.6f2
  • Версия Firebase Unity SDK: 12.5.0
  • Целевая платформа: Android
  • Код: Выделить всё

    Application.internetReachability
    правильно сообщает NotReachable в автономном режиме.
    Аутентификация Firebase успешно сохраняет идентификатор пользователя.


Подробнее здесь: https://stackoverflow.com/questions/797 ... -with-exis
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «C#»