254 lines
No EOL
9.9 KiB
C#
254 lines
No EOL
9.9 KiB
C#
#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 |