480 lines
No EOL
17 KiB
C#
480 lines
No EOL
17 KiB
C#
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using ShiroginSDK.Runtime.Core.SDK;
|
|
using ShiroginSDK.Runtime.Core.Utils;
|
|
using ShiroginSDK.Runtime.Modules.Data.Scripts;
|
|
using ShiroginSDK.Runtime.Modules.Events;
|
|
using ShiroginSDK.Runtime.Services.Base;
|
|
using ShiroginSDK.Runtime.Services.Interfaces;
|
|
using UnityEngine;
|
|
|
|
#if SHIROGIN_FIREBASE_REMOTE_CONFIG
|
|
using Firebase.RemoteConfig;
|
|
#endif
|
|
|
|
namespace ShiroginSDK.Runtime.Services.Implementations.RemoteConfig
|
|
{
|
|
#if SHIROGIN_FIREBASE_REMOTE_CONFIG
|
|
/// <summary>
|
|
/// Fetches and caches Firebase Remote Config data for ShiroginSDK.
|
|
/// Automatically reads selected group and enable flags from SDKConfig.
|
|
/// </summary>
|
|
public class RemoteConfigService : ServiceBase, IRemoteConfigService
|
|
{
|
|
private ShiroginConfig _config;
|
|
private string _configKey;
|
|
private bool _initialized;
|
|
|
|
// ---------------------------------------------------------
|
|
// Value Accessors
|
|
// ---------------------------------------------------------
|
|
private string CachedJson
|
|
{
|
|
get
|
|
{
|
|
var dataService = ServiceLocator.Get<IDataService>();
|
|
return dataService?.Get<RemoteConfigData>()?.CachedJson ?? "{}";
|
|
}
|
|
}
|
|
|
|
public void ForceRefresh()
|
|
{
|
|
CoroutineRunner.Start(FetchAndCacheRoutine());
|
|
}
|
|
|
|
public int GetInt(string key, int defaultValue = 0)
|
|
{
|
|
if (TryGetValue(key, out var raw) && int.TryParse(raw, out var val))
|
|
return val;
|
|
return defaultValue;
|
|
}
|
|
|
|
public float GetFloat(string key, float defaultValue = 0f)
|
|
{
|
|
if (TryGetValue(key, out var raw) && float.TryParse(raw, out var val))
|
|
return val;
|
|
return defaultValue;
|
|
}
|
|
|
|
public bool GetBool(string key, bool defaultValue = false)
|
|
{
|
|
if (TryGetValue(key, out var raw))
|
|
return raw.Equals("true", StringComparison.OrdinalIgnoreCase);
|
|
return defaultValue;
|
|
}
|
|
|
|
public string GetString(string key, string defaultValue = "")
|
|
{
|
|
if (TryGetValue(key, out var raw))
|
|
return raw;
|
|
return defaultValue;
|
|
}
|
|
|
|
// ---------------------------------------------------------
|
|
// Lifecycle
|
|
// ---------------------------------------------------------
|
|
protected override void OnInitialize()
|
|
{
|
|
_config = ShiroginConfig.Load();
|
|
|
|
if (_config == null)
|
|
{
|
|
Debug.LogError("[RemoteConfigService] ❌ SDKConfig not found. Cannot initialize Remote Config.");
|
|
return;
|
|
}
|
|
|
|
if (!_config.enableFirebaseRemoteConfig)
|
|
{
|
|
Debug.Log("[RemoteConfigService] ⚙️ Firebase Remote Config disabled in SDKConfig.");
|
|
return;
|
|
}
|
|
|
|
_configKey = _config.selectedRemoteGroup;
|
|
if (string.IsNullOrEmpty(_configKey))
|
|
_configKey = "DefaultGroup";
|
|
|
|
var eventService = ServiceLocator.Get<IEventService>();
|
|
eventService.Subscribe<ShiroginEvents.Firebase.FirebaseReadyEvent>(OnFirebaseReady);
|
|
|
|
Debug.Log($"[RemoteConfigService] ✅ Initialized. Waiting for Firebase. Target Group: '{_configKey}'");
|
|
}
|
|
|
|
protected override void OnDispose()
|
|
{
|
|
var eventService = ServiceLocator.Get<IEventService>();
|
|
eventService.Unsubscribe<ShiroginEvents.Firebase.FirebaseReadyEvent>(OnFirebaseReady);
|
|
}
|
|
|
|
// ---------------------------------------------------------
|
|
// Firebase Ready
|
|
// ---------------------------------------------------------
|
|
private void OnFirebaseReady(ShiroginEvents.Firebase.FirebaseReadyEvent _)
|
|
{
|
|
if (!_config.enableFirebaseRemoteConfig)
|
|
{
|
|
Debug.Log("[RemoteConfigService] ⚙️ Firebase Remote Config disabled — skipping fetch.");
|
|
return;
|
|
}
|
|
|
|
_initialized = true;
|
|
Debug.Log($"[RemoteConfigService] 🔥 Firebase ready, fetching key '{_configKey}'...");
|
|
CoroutineRunner.Start(DelayedStart());
|
|
}
|
|
|
|
private IEnumerator DelayedStart()
|
|
{
|
|
yield return new WaitForSeconds(1f);
|
|
TryFetch();
|
|
}
|
|
|
|
// ---------------------------------------------------------
|
|
// Fetch Logic
|
|
// ---------------------------------------------------------
|
|
private void TryFetch()
|
|
{
|
|
var dataService = ServiceLocator.Get<IDataService>();
|
|
var data = dataService?.Get<RemoteConfigData>();
|
|
if (data == null)
|
|
{
|
|
Debug.LogError("[RemoteConfigService] ❌ RemoteConfigData not found in DataService!");
|
|
return;
|
|
}
|
|
|
|
if (!data.ShouldFetchNewData())
|
|
{
|
|
Debug.Log("[RemoteConfigService] 🕓 Using cached Remote Config data (less than 24h old).");
|
|
TriggerEvent(data.CachedJson);
|
|
return;
|
|
}
|
|
|
|
CoroutineRunner.Start(FetchAndCacheRoutine());
|
|
}
|
|
|
|
public void Initialize(string selectedGroup)
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
private IEnumerator FetchAndCacheRoutine()
|
|
{
|
|
if (!_initialized)
|
|
{
|
|
Debug.LogWarning("[RemoteConfigService] ⚠️ Firebase not initialized yet.");
|
|
yield break;
|
|
}
|
|
|
|
Debug.Log($"[RemoteConfigService] 🔄 Fetching Remote Config key '{_configKey}'...");
|
|
var fetchTask = FirebaseRemoteConfig.DefaultInstance.FetchAsync(TimeSpan.Zero);
|
|
var start = Time.realtimeSinceStartup;
|
|
const float TIMEOUT = 10f;
|
|
|
|
while (!fetchTask.IsCompleted && Time.realtimeSinceStartup - start < TIMEOUT)
|
|
yield return null;
|
|
|
|
if (!fetchTask.IsCompleted)
|
|
{
|
|
Debug.LogWarning("[RemoteConfigService] ⚠️ Fetch timeout. Using cached data.");
|
|
TriggerEvent(ServiceLocator.Get<IDataService>()?.Get<RemoteConfigData>()?.CachedJson ?? "{}");
|
|
yield break;
|
|
}
|
|
|
|
if (fetchTask.IsFaulted)
|
|
{
|
|
Debug.LogError(
|
|
$"[RemoteConfigService] ❌ Fetch error: {fetchTask.Exception?.GetBaseException().Message}");
|
|
TriggerEvent(ServiceLocator.Get<IDataService>()?.Get<RemoteConfigData>()?.CachedJson ?? "{}");
|
|
yield break;
|
|
}
|
|
|
|
var activateTask = FirebaseRemoteConfig.DefaultInstance.ActivateAsync();
|
|
while (!activateTask.IsCompleted) yield return null;
|
|
|
|
ExtractAndCacheValues();
|
|
Debug.Log("RemoteConfig Service Dummy");
|
|
}
|
|
|
|
// ---------------------------------------------------------
|
|
// Extract + Cache + Reload
|
|
// ---------------------------------------------------------
|
|
private void ExtractAndCacheValues()
|
|
{
|
|
try
|
|
{
|
|
var rawJson = FirebaseRemoteConfig.DefaultInstance.GetValue(_configKey).StringValue;
|
|
|
|
if (string.IsNullOrEmpty(rawJson))
|
|
{
|
|
Debug.LogWarning($"[RemoteConfigService] ⚠️ RemoteConfig key '{_configKey}' returned empty JSON.");
|
|
return;
|
|
}
|
|
|
|
var dataService = ServiceLocator.Get<IDataService>();
|
|
var data = dataService?.Get<RemoteConfigData>();
|
|
if (data == null)
|
|
{
|
|
Debug.LogError("[RemoteConfigService] ❌ RemoteConfigData not found in DataService!");
|
|
return;
|
|
}
|
|
|
|
if (data.CachedJson == rawJson)
|
|
{
|
|
Debug.Log("[RemoteConfigService] 🔁 No changes detected. Cached data is up-to-date.");
|
|
return;
|
|
}
|
|
|
|
data.UpdateCache(rawJson);
|
|
|
|
try
|
|
{
|
|
data.Load();
|
|
data.Initialize();
|
|
Debug.Log("[RemoteConfigService] 🔁 RemoteConfigData re-initialized with latest Firebase values.");
|
|
}
|
|
catch (Exception reinitEx)
|
|
{
|
|
Debug.LogError($"[RemoteConfigService] ❌ RemoteConfigData re-init failed: {reinitEx.Message}");
|
|
}
|
|
|
|
TriggerEvent(rawJson);
|
|
Debug.Log($"[RemoteConfigService] ✅ Remote Config key '{_configKey}' updated and applied.");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Debug.LogError($"[RemoteConfigService] ❌ ExtractAndCacheValues failed: {ex.Message}");
|
|
}
|
|
}
|
|
|
|
// ---------------------------------------------------------
|
|
// Event Dispatch
|
|
// ---------------------------------------------------------
|
|
private void TriggerEvent(string json)
|
|
{
|
|
try
|
|
{
|
|
var eventService = ServiceLocator.Get<IEventService>();
|
|
eventService.Invoke(
|
|
new ShiroginEvents.RemoteConfig.RemoteConfigUpdatedEvent(json, new Dictionary<string, string>()));
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Debug.LogError($"[RemoteConfigService] ⚠️ RemoteConfigUpdatedEvent failed: {ex.Message}");
|
|
}
|
|
}
|
|
|
|
private bool TryGetValue(string key, out string value)
|
|
{
|
|
value = string.Empty;
|
|
var json = CachedJson;
|
|
if (string.IsNullOrEmpty(json) || string.IsNullOrEmpty(key))
|
|
return false;
|
|
|
|
key = $"\"{key}\"";
|
|
var index = json.IndexOf(key, StringComparison.Ordinal);
|
|
if (index == -1) return false;
|
|
|
|
var colon = json.IndexOf(':', index);
|
|
if (colon == -1) return false;
|
|
|
|
var comma = json.IndexOf(',', colon);
|
|
var end = comma == -1 ? json.IndexOf('}', colon) : comma;
|
|
if (end == -1) end = json.Length - 1;
|
|
|
|
var raw = json.Substring(colon + 1, end - colon - 1).Trim().Trim('"');
|
|
value = raw;
|
|
return true;
|
|
}
|
|
}
|
|
#else
|
|
/// <summary>
|
|
/// Fetches and caches Firebase Remote Config data for ShiroginSDK.
|
|
/// Automatically reads selected group and enable flags from SDKConfig.
|
|
/// </summary>
|
|
public class RemoteConfigService : ServiceBase, IRemoteConfigService
|
|
{
|
|
private ShiroginConfig _config;
|
|
private string _configKey;
|
|
private bool _initialized;
|
|
|
|
// ---------------------------------------------------------
|
|
// Value Accessors
|
|
// ---------------------------------------------------------
|
|
private string CachedJson
|
|
{
|
|
get
|
|
{
|
|
var dataService = ServiceLocator.Get<IDataService>();
|
|
return dataService?.Get<RemoteConfigData>()?.CachedJson ?? "{}";
|
|
}
|
|
}
|
|
|
|
public void ForceRefresh()
|
|
{
|
|
CoroutineRunner.Start(FetchAndCacheRoutine());
|
|
}
|
|
|
|
public int GetInt(string key, int defaultValue = 0)
|
|
{
|
|
if (TryGetValue(key, out var raw) && int.TryParse(raw, out var val))
|
|
return val;
|
|
return defaultValue;
|
|
}
|
|
|
|
public float GetFloat(string key, float defaultValue = 0f)
|
|
{
|
|
if (TryGetValue(key, out var raw) && float.TryParse(raw, out var val))
|
|
return val;
|
|
return defaultValue;
|
|
}
|
|
|
|
public bool GetBool(string key, bool defaultValue = false)
|
|
{
|
|
if (TryGetValue(key, out var raw))
|
|
return raw.Equals("true", StringComparison.OrdinalIgnoreCase);
|
|
return defaultValue;
|
|
}
|
|
|
|
public string GetString(string key, string defaultValue = "")
|
|
{
|
|
if (TryGetValue(key, out var raw))
|
|
return raw;
|
|
return defaultValue;
|
|
}
|
|
|
|
// ---------------------------------------------------------
|
|
// Lifecycle
|
|
// ---------------------------------------------------------
|
|
protected override void OnInitialize()
|
|
{
|
|
_config = ShiroginConfig.Load();
|
|
|
|
if (_config == null)
|
|
{
|
|
Debug.LogError("[RemoteConfigService] ❌ SDKConfig not found. Cannot initialize Remote Config.");
|
|
return;
|
|
}
|
|
|
|
_configKey = _config.selectedRemoteGroup;
|
|
if (string.IsNullOrEmpty(_configKey))
|
|
_configKey = "DefaultGroup";
|
|
|
|
var eventService = ServiceLocator.Get<IEventService>();
|
|
eventService.Subscribe<ShiroginEvents.Firebase.FirebaseReadyEvent>(OnFirebaseReady);
|
|
|
|
Debug.Log($"[RemoteConfigService] Dummy Initialized. Waiting for Firebase. Target Group: '{_configKey}'");
|
|
}
|
|
|
|
protected override void OnDispose()
|
|
{
|
|
var eventService = ServiceLocator.Get<IEventService>();
|
|
eventService.Unsubscribe<ShiroginEvents.Firebase.FirebaseReadyEvent>(OnFirebaseReady);
|
|
}
|
|
|
|
// ---------------------------------------------------------
|
|
// Firebase Ready
|
|
// ---------------------------------------------------------
|
|
private void OnFirebaseReady(ShiroginEvents.Firebase.FirebaseReadyEvent _)
|
|
{
|
|
|
|
_initialized = true;
|
|
Debug.Log($"[RemoteConfigService] Dummy Firebase ready, fetching key '{_configKey}'...");
|
|
CoroutineRunner.Start(DelayedStart());
|
|
}
|
|
|
|
private IEnumerator DelayedStart()
|
|
{
|
|
yield return new WaitForSeconds(1f);
|
|
TryFetch();
|
|
}
|
|
|
|
// ---------------------------------------------------------
|
|
// Fetch Logic
|
|
// ---------------------------------------------------------
|
|
private void TryFetch()
|
|
{
|
|
var dataService = ServiceLocator.Get<IDataService>();
|
|
var data = dataService?.Get<RemoteConfigData>();
|
|
if (data == null)
|
|
{
|
|
Debug.LogError("[RemoteConfigService] ❌ RemoteConfigData not found in DataService!");
|
|
return;
|
|
}
|
|
|
|
if (!data.ShouldFetchNewData())
|
|
{
|
|
Debug.Log("[RemoteConfigService] 🕓 Using cached Remote Config data (less than 24h old).");
|
|
TriggerEvent(data.CachedJson);
|
|
return;
|
|
}
|
|
|
|
CoroutineRunner.Start(FetchAndCacheRoutine());
|
|
}
|
|
|
|
public void Initialize(string selectedGroup)
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
private IEnumerator FetchAndCacheRoutine()
|
|
{
|
|
if (!_initialized)
|
|
{
|
|
Debug.LogWarning("[RemoteConfigService] ⚠️ Firebase not initialized yet.");
|
|
yield break;
|
|
}
|
|
Debug.Log("RemoteConfigService Dummy FetchAndCacheRoutine");
|
|
ExtractAndCacheValues();
|
|
Debug.Log("RemoteConfig Service Dummy");
|
|
}
|
|
|
|
// ---------------------------------------------------------
|
|
// Extract + Cache + Reload
|
|
// ---------------------------------------------------------
|
|
private void ExtractAndCacheValues()
|
|
{
|
|
Debug.Log("RemoteConfigService Dummy ExtractAndCacheValues");
|
|
|
|
}
|
|
|
|
// ---------------------------------------------------------
|
|
// Event Dispatch
|
|
// ---------------------------------------------------------
|
|
private void TriggerEvent(string json)
|
|
{
|
|
try
|
|
{
|
|
var eventService = ServiceLocator.Get<IEventService>();
|
|
eventService.Invoke(
|
|
new ShiroginEvents.RemoteConfig.RemoteConfigUpdatedEvent(json, new Dictionary<string, string>()));
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Debug.LogError($"[RemoteConfigService] ⚠️ RemoteConfigUpdatedEvent failed: {ex.Message}");
|
|
}
|
|
}
|
|
|
|
private bool TryGetValue(string key, out string value)
|
|
{
|
|
value = string.Empty;
|
|
var json = CachedJson;
|
|
if (string.IsNullOrEmpty(json) || string.IsNullOrEmpty(key))
|
|
return false;
|
|
|
|
key = $"\"{key}\"";
|
|
var index = json.IndexOf(key, StringComparison.Ordinal);
|
|
if (index == -1) return false;
|
|
|
|
var colon = json.IndexOf(':', index);
|
|
if (colon == -1) return false;
|
|
|
|
var comma = json.IndexOf(',', colon);
|
|
var end = comma == -1 ? json.IndexOf('}', colon) : comma;
|
|
if (end == -1) end = json.Length - 1;
|
|
|
|
var raw = json.Substring(colon + 1, end - colon - 1).Trim().Trim('"');
|
|
value = raw;
|
|
return true;
|
|
}
|
|
}
|
|
#endif
|
|
} |