Playvoi/Assets/ShiroginSDK/Editor/RemoteConfig/RemoteConfigDefinitionEditor.cs

254 lines
No EOL
9.9 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#if UNITY_EDITOR
using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Text.RegularExpressions;
using ShiroginSDK.Runtime.Core.SDK;
using ShiroginSDK.Runtime.Modules.Data.Scripts;
using ShiroginSDK.Runtime.Modules.RemoteConfig.Scripts.SO;
using ShiroginSDK.Runtime.Shared.Constants;
using UnityEditor;
using UnityEngine;
namespace ShiroginSDK.Editor.RemoteConfig
{
[CustomEditor(typeof(RemoteConfigDefinition))]
public class RemoteConfigDefinitionEditor : UnityEditor.Editor
{
private bool showRemotePreview;
public override void OnInspectorGUI()
{
var def = (RemoteConfigDefinition)target;
// Auto sync group name with SDKConfig
var sdkConfig = Resources.Load<ShiroginConfig>(SDKConstants.ConfigResourcePath);
if (sdkConfig != null && def.GroupName != sdkConfig.selectedRemoteGroup)
{
def.SetGroupName(sdkConfig.selectedRemoteGroup);
EditorUtility.SetDirty(def);
}
EditorGUILayout.Space(6);
EditorGUILayout.LabelField("Remote Config Info", EditorStyles.boldLabel);
using (new EditorGUI.DisabledScope(true))
{
EditorGUILayout.TextField("Group Name", def.GroupName);
}
EditorGUILayout.Space(8);
DrawDefaultInspector();
EditorGUILayout.Space(8);
EditorGUILayout.LabelField("Remote Config Tools", EditorStyles.boldLabel);
using (new EditorGUILayout.HorizontalScope())
{
if (GUILayout.Button("✅ Validate Keys"))
{
if (def.ValidateKeys(out var err))
EditorUtility.DisplayDialog("Validation", "All keys valid ✅", "OK");
else
EditorUtility.DisplayDialog("Validation Failed", err, "OK");
}
if (GUILayout.Button("🔤 Sort by Key"))
{
Undo.RecordObject(def, "Sort Remote Config Entries");
def.Entries = def.Entries.OrderBy(e => e.Key).ToList();
EditorUtility.SetDirty(def);
}
}
if (GUILayout.Button("📋 Copy JSON to Clipboard"))
{
if (!def.ValidateKeys(out var err))
{
EditorUtility.DisplayDialog("Error", err, "OK");
return;
}
var json = def.GenerateJson();
EditorGUIUtility.systemCopyBuffer = json;
Debug.Log($"[RemoteConfig] JSON copied to clipboard ({json.Length} chars)");
EditorUtility.DisplayDialog("Success", "JSON copied to clipboard ✅", "OK");
}
// 🔸 Import button
if (GUILayout.Button("⬆️ Import from RemoteConfigData")) ImportFromRemoteConfigData(def);
// 🌐 Remote Data Preview (Cached)
EditorGUILayout.Space(12);
showRemotePreview = EditorGUILayout.Foldout(showRemotePreview, "🌐 Remote Data Preview (Cached)", true);
if (showRemotePreview)
{
if (GUILayout.Button("🔄 Refresh from Cache"))
{
def.RefreshFromCache();
Debug.Log("[RemoteConfigDefinition] 🔄 Refreshed from cached RemoteConfigData.");
}
if (def.RemoteEntries == null || def.RemoteEntries.Count == 0)
{
EditorGUILayout.HelpBox("No cached remote data found.", MessageType.Info);
}
else
{
EditorGUILayout.Space(4);
EditorGUILayout.LabelField("Cached Entries:", EditorStyles.boldLabel);
EditorGUILayout.BeginVertical("box");
foreach (var entry in def.RemoteEntries)
{
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField(entry.Key, GUILayout.Width(160));
switch (entry.Type)
{
case RemoteConfigDefinition.ValueType.Boolean:
EditorGUILayout.Toggle(entry.BoolValue);
break;
case RemoteConfigDefinition.ValueType.Number:
EditorGUILayout.LabelField(entry.NumberValue.ToString(CultureInfo.InvariantCulture));
break;
default:
EditorGUILayout.LabelField(entry.StringValue);
break;
}
EditorGUILayout.EndHorizontal();
}
EditorGUILayout.EndVertical();
}
}
if (GUI.changed)
EditorUtility.SetDirty(def);
}
/// <summary>
/// Imports instance fields from RemoteConfigData as RemoteConfigDefinition entries.
/// Works with BaseData-based RemoteConfigData (non-static).
/// </summary>
private void ImportFromRemoteConfigData(RemoteConfigDefinition def)
{
// Onay penceresi
var proceed = EditorUtility.DisplayDialog(
"Confirm Import",
"Are you sure you want to import all fields from RemoteConfigData?\n\nThis will overwrite current entries.",
"Yes, Import",
"Cancel"
);
if (!proceed)
{
Debug.Log("[RemoteConfigDefinitionEditor] 🚫 Import canceled by user.");
return;
}
Undo.RecordObject(def, "Import from RemoteConfigData");
def.Entries.Clear();
var remoteDataType = typeof(RemoteConfigData);
// Public INSTANCE fields (BaseData yapısıyla uyumlu)
var fields = remoteDataType.GetFields(BindingFlags.Public |
BindingFlags.Instance);
if (fields == null || fields.Length == 0)
{
Debug.LogWarning(
"[RemoteConfigDefinitionEditor] ⚠️ RemoteConfigData içinde public instance field bulunamadı.");
return;
}
// Geçici örnek yarat (BaseData default değerlerini okumak için)
var instance = Activator.CreateInstance(remoteDataType);
var ci = CultureInfo.InvariantCulture;
foreach (var f in fields)
{
if (f.IsLiteral || f.IsInitOnly) continue; // const/readonly atla
var entry = new RemoteConfigDefinition.Entry();
entry.Key = ToSnakeCasePreserveUnderscores(f.Name); // 🔸 snake_case + '_' korunur
var val = f.GetValue(instance);
if (val == null)
{
entry.Type = RemoteConfigDefinition.ValueType.String;
entry.StringValue = string.Empty;
}
else if (val is bool b)
{
entry.Type = RemoteConfigDefinition.ValueType.Boolean;
entry.BoolValue = b;
}
else if (val is int i)
{
entry.Type = RemoteConfigDefinition.ValueType.Number;
entry.NumberValue = i;
}
else if (val is float fl)
{
entry.Type = RemoteConfigDefinition.ValueType.Number;
entry.NumberValue = fl;
}
else if (val is Array arr)
{
// Dizileri "1,1.4,2" şeklinde string'e çevir
var values = arr.Cast<object>().Select(x => Convert.ToString(x, ci)).ToArray();
entry.Type = RemoteConfigDefinition.ValueType.String;
entry.StringValue = string.Join(",", values);
}
else if (val is IEnumerable list && !(val is string))
{
// Listeleri string'e çevir
var values = new List<string>();
foreach (var item in list)
values.Add(Convert.ToString(item, ci));
entry.Type = RemoteConfigDefinition.ValueType.String;
entry.StringValue = string.Join(",", values);
}
else
{
entry.Type = RemoteConfigDefinition.ValueType.String;
entry.StringValue = val.ToString();
}
def.Entries.Add(entry);
}
def.Entries = def.Entries.OrderBy(e => e.Key).ToList();
EditorUtility.SetDirty(def);
Debug.Log($"[RemoteConfigDefinitionEditor] ✅ Imported {def.Entries.Count} entries from RemoteConfigData.");
EditorUtility.DisplayDialog("Import Completed",
$"Successfully imported {def.Entries.Count} entries from RemoteConfigData.", "OK");
}
// Pascal/camelCase → snake_case, mevcut '_' karakterlerini KORUR
// Örn: "TargetFps" -> "target_fps", "Enemy_HpMultiplier" -> "enemy_hp_multiplier"
private static string ToSnakeCasePreserveUnderscores(string input)
{
if (string.IsNullOrEmpty(input)) return input;
// Büyük harf grupları ve geçişleri doğru bölmek için regex
// (mevcut '_' karakterlerine dokunmuyoruz)
var s = Regex.Replace(input, "([A-Z]+)([A-Z][a-z])", "$1_$2");
s = Regex.Replace(s, "([a-z0-9])([A-Z])", "$1_$2");
// Çift alt çizgileri tek'e indir (varsa)
while (s.Contains("__")) s = s.Replace("__", "_");
return s.ToLowerInvariant();
}
}
}
#endif