Skip to content

Commit 0ef3f6b

Browse files
committed
FirebaseRemoteConfigHelper
1 parent ffdb4dd commit 0ef3f6b

File tree

3 files changed

+213
-0
lines changed

3 files changed

+213
-0
lines changed

FirebaseAppHelper.cs

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
#if UNITY_EDITOR
2+
using Firebase;
3+
using UnityEngine;
4+
using System.Reflection;
5+
6+
namespace RimuruDev
7+
{
8+
public static class FirebaseAppHelper
9+
{
10+
// NOTE:
11+
// В общем не нашел как можно вызвать FirebaseApp.DisposeAllApps() по этому вызываю через рефлекшен.
12+
// *
13+
// У меня не получилось грамотно словари подчистить, по этому этот вариант выбрал, что бы наверняка не подтягивать кеши.
14+
// *
15+
// Так выглядит метод DisposeAllApps() у класса FirebaseApp:
16+
//
17+
// public sealed class FirebaseApp : IDisposable
18+
// {
19+
// internal static void DisposeAllApps()
20+
// {
21+
// List<FirebaseApp> firebaseAppList = new List<FirebaseApp>();
22+
// lock (FirebaseApp.nameToProxy)
23+
// {
24+
// foreach (KeyValuePair<string, FirebaseApp> keyValuePair in FirebaseApp.nameToProxy)
25+
// firebaseAppList.Add(keyValuePair.Value);
26+
// }
27+
// foreach (FirebaseApp firebaseApp in firebaseAppList)
28+
// firebaseApp.Dispose();
29+
// }
30+
// }
31+
public static void DisposeAllFirebaseApps()
32+
{
33+
var type = typeof(FirebaseApp);
34+
35+
var method = type.GetMethod("DisposeAllApps", BindingFlags.Static | BindingFlags.NonPublic);
36+
37+
if (method != null)
38+
method.Invoke(null, null);
39+
else
40+
Debug.LogWarning("<color=red>DisposeAllApps method not found.</color>");
41+
}
42+
}
43+
}
44+
#endif

FirebaseRemoteConfigHelper.cs

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
#if UNITY_EDITOR
2+
using UnityEngine;
3+
using System.Reflection;
4+
using Firebase.RemoteConfig;
5+
using System.Collections.Generic;
6+
7+
namespace RimuruDev
8+
{
9+
public static class FirebaseRemoteConfigHelper
10+
{
11+
public static void PrintRemoteConfigCache()
12+
{
13+
var type = typeof(FirebaseRemoteConfig);
14+
var field = type.GetField("remoteConfigByInstanceKey", BindingFlags.Static | BindingFlags.NonPublic);
15+
16+
if (field != null)
17+
{
18+
if (field.GetValue(null) is IDictionary<string, FirebaseRemoteConfig> dictionary)
19+
{
20+
Debug.Log($"<color=yellow>RemoteConfig cache count: {dictionary.Count}</color>");
21+
22+
foreach (var kvp in dictionary)
23+
Debug.Log($"<color=yellow>Key: {kvp.Key}, Instance: {kvp.Value}</color>");
24+
}
25+
else
26+
Debug.LogWarning("<color=yellow>Dictionary remoteConfigByInstanceKey == null.</color>");
27+
}
28+
else
29+
Debug.LogWarning("<color=red>Field remoteConfigByInstanceKey not found.</color>");
30+
}
31+
32+
public static void ClearRemoteConfigCache()
33+
{
34+
// NOTE:
35+
// Словарик забетонирован, по этому пришлось действовать немного по плохому.
36+
// *
37+
// Так выглядит словарик remoteConfigByInstanceKey у FirebaseRemoteConfig:
38+
//
39+
// public sealed class FirebaseRemoteConfig
40+
// {
41+
// private static readonly Dictionary<string, FirebaseRemoteConfig> remoteConfigByInstanceKey = new Dictionary<string, FirebaseRemoteConfig>();
42+
// }
43+
var type = typeof(FirebaseRemoteConfig);
44+
var field = type.GetField("remoteConfigByInstanceKey", BindingFlags.Static | BindingFlags.NonPublic);
45+
46+
if (field != null)
47+
{
48+
var dictionary = field.GetValue(null) as IDictionary<string, FirebaseRemoteConfig>;
49+
dictionary?.Clear();
50+
51+
Debug.Log("<color=yellow>FirebaseRemoteConfig cache cleared.</color>");
52+
}
53+
else
54+
Debug.LogWarning("<color=red>Field remoteConfigByInstanceKey not found.</color>");
55+
}
56+
}
57+
}
58+
#endif

RemoteConfigEditor.cs

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
#if UNITY_EDITOR
2+
using System;
3+
using Firebase;
4+
using UnityEditor;
5+
using UnityEngine;
6+
using Firebase.RemoteConfig;
7+
using System.Threading.Tasks;
8+
9+
namespace RimuruDev
10+
{
11+
public sealed class RemoteConfigEditor : EditorWindow
12+
{
13+
private string log;
14+
private bool firebaseInitialized;
15+
16+
[MenuItem("RimuruDev Tools/Firebase RemoteConfig Editor")]
17+
private static void ShowWindow() =>
18+
GetWindow<RemoteConfigEditor>("RemoteConfig Editor");
19+
20+
private async void OnEnable()
21+
{
22+
try
23+
{
24+
var result = await FirebaseApp.CheckAndFixDependenciesAsync();
25+
26+
if (result == DependencyStatus.Available)
27+
{
28+
firebaseInitialized = true;
29+
log += "\nFirebase initialized in Editor";
30+
}
31+
else
32+
{
33+
log += $"\nCould not resolve Firebase dependencies: {result}";
34+
}
35+
}
36+
catch (Exception e)
37+
{
38+
Debug.LogError($"TODO: handle exception");
39+
}
40+
}
41+
42+
private void OnGUI()
43+
{
44+
GUILayout.Label("Remote Config Editor", EditorStyles.boldLabel);
45+
GUILayout.Label(log, EditorStyles.wordWrappedLabel);
46+
47+
EditorGUI.BeginDisabledGroup(!firebaseInitialized);
48+
49+
if (GUILayout.Button("Print RemoteConfig Cache"))
50+
FirebaseRemoteConfigHelper.PrintRemoteConfigCache();
51+
52+
if (GUILayout.Button("Restart Firebase and Fetch Remote Config"))
53+
_ = RestartFirebaseAndFetchAsync();
54+
55+
EditorGUI.EndDisabledGroup();
56+
}
57+
58+
private async Task RestartFirebaseAndFetchAsync()
59+
{
60+
try
61+
{
62+
FirebaseRemoteConfigHelper.PrintRemoteConfigCache();
63+
FirebaseRemoteConfigHelper.ClearRemoteConfigCache();
64+
FirebaseAppHelper.DisposeAllFirebaseApps();
65+
66+
// NOTE: Ну я на всякий пожарный подожду, мало ли не успеет прогреться :D
67+
await Task.Delay(500);
68+
69+
var result = await FirebaseApp.CheckAndFixDependenciesAsync();
70+
if (result != DependencyStatus.Available)
71+
{
72+
log += $"\nCould not reinitialize Firebase: {result}";
73+
Repaint();
74+
return;
75+
}
76+
77+
firebaseInitialized = true;
78+
log += "\nFirebase reinitialized in Editor";
79+
80+
// NOTE: Прогрев перед подтягиванием свежачка с сервачка :3
81+
var config = FirebaseRemoteConfig.DefaultInstance;
82+
var settings = config.ConfigSettings;
83+
84+
settings.MinimumFetchIntervalInMilliseconds = 0;
85+
settings.FetchTimeoutInMilliseconds = 30000;
86+
87+
await config.SetConfigSettingsAsync(settings);
88+
89+
await config.EnsureInitializedAsync();
90+
91+
// NOTE: Получаем свежачок, и кайфуем.
92+
await config.FetchAsync(TimeSpan.Zero);
93+
await config.ActivateAsync();
94+
95+
// NOTE: Мега лень париться с StringBuilder, один-фиг редактор.
96+
log += $"\nFetch succeeded! Time: {config.Info.FetchTime}";
97+
log += $"\nAll values count: {config.AllValues.Count}";
98+
99+
foreach (var pair in config.AllValues)
100+
log += $"\n[ Key: {pair.Key} | Value: {pair.Value.StringValue} ]";
101+
}
102+
catch (Exception e)
103+
{
104+
log += $"\nRestart/Fetch failed: {e}";
105+
}
106+
107+
Repaint();
108+
}
109+
}
110+
}
111+
#endif

0 commit comments

Comments
 (0)