Playvoi/Assets/ShiroginSDK/Runtime/Services/Implementations/RemoteConfig/RemoteConfigService.cs

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
}