Playvoi/Assets/ShiroginSDK/Runtime/Modules/RemoteConfig/Scripts/SO/RemoteConfigDefinition.cs

207 lines
No EOL
6.9 KiB
C#

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Text;
using ShiroginSDK.Runtime.Modules.Data.Scripts;
using ShiroginSDK.Runtime.Services.Base;
using ShiroginSDK.Runtime.Services.Interfaces;
using UnityEngine;
namespace ShiroginSDK.Runtime.Modules.RemoteConfig.Scripts.SO
{
[CreateAssetMenu(fileName = "RemoteConfigDefinition", menuName = "ShiroginSDK/Remote Config/Definition")]
public class RemoteConfigDefinition : ScriptableObject
{
public enum ValueType
{
String,
Number,
Boolean
}
[SerializeField] [HideInInspector]
private string _groupName = "DefaultGroup";
[Header("Game-specific Remote Config key-value pairs")]
public List<Entry> Entries = new();
[NonSerialized]
public List<Entry> RemoteEntries = new();
public string GroupName => _groupName;
public void RefreshFromCache()
{
RemoteEntries.Clear();
var dataService = ServiceLocator.Get<IDataService>();
var data = dataService?.Get<RemoteConfigData>();
if (data == null || string.IsNullOrEmpty(data.CachedJson) || data.CachedJson == "{}")
{
Debug.Log("[RemoteConfigDefinition] ⚠️ No cached Remote Config data found.");
return;
}
try
{
var json = data.CachedJson.Trim();
var groupStart = json.IndexOf('{', 1);
var groupEnd = json.LastIndexOf('}');
if (groupStart < 0 || groupEnd <= groupStart)
{
Debug.LogWarning("[RemoteConfigDefinition] ⚠️ Invalid JSON format.");
return;
}
var inner = json.Substring(groupStart + 1, groupEnd - groupStart - 1).Trim();
if (inner.StartsWith("{")) inner = inner.Substring(1);
if (inner.EndsWith("}")) inner = inner.Substring(0, inner.Length - 1);
var pairs = inner.Split(',');
foreach (var pair in pairs)
{
var kv = pair.Split(':');
if (kv.Length < 2) continue;
var key = kv[0].Trim().Trim('"');
var rawValue = kv[1].Trim().Trim('"');
var entry = new Entry { Key = key };
if (rawValue.Equals("true", StringComparison.OrdinalIgnoreCase) ||
rawValue.Equals("false", StringComparison.OrdinalIgnoreCase))
{
entry.Type = ValueType.Boolean;
entry.BoolValue = rawValue.Equals("true", StringComparison.OrdinalIgnoreCase);
}
else if (double.TryParse(rawValue, NumberStyles.Any, CultureInfo.InvariantCulture, out var num))
{
entry.Type = ValueType.Number;
entry.NumberValue = num;
}
else
{
entry.Type = ValueType.String;
entry.StringValue = rawValue;
}
RemoteEntries.Add(entry);
}
Debug.Log(
$"[RemoteConfigDefinition] ✅ Parsed {RemoteEntries.Count} entries from cached RemoteConfigData.");
}
catch (Exception ex)
{
Debug.LogError($"[RemoteConfigDefinition] ❌ Failed to parse cache: {ex.Message}");
}
}
public string GenerateJson(bool pretty = true)
{
var nl = pretty ? "\n" : string.Empty;
var ind = pretty ? " " : string.Empty;
var sb = new StringBuilder();
sb.Append('{').Append(nl);
sb.Append(ind).Append('"').Append(_groupName).Append('"').Append(": {").Append(nl);
for (var i = 0; i < Entries.Count; i++)
{
var e = Entries[i];
if (string.IsNullOrWhiteSpace(e.Key)) continue;
sb.Append(ind).Append(ind)
.Append('"').Append(Escape(e.Key)).Append('"')
.Append(": ");
switch (e.Type)
{
case ValueType.String:
sb.Append('"').Append(Escape(e.StringValue ?? string.Empty)).Append('"');
break;
case ValueType.Number:
sb.Append(e.NumberValue.ToString(CultureInfo.InvariantCulture));
break;
case ValueType.Boolean:
sb.Append(e.BoolValue ? "true" : "false");
break;
}
if (i < Entries.Count - 1)
sb.Append(',');
sb.Append(nl);
}
sb.Append(ind).Append('}').Append(nl).Append('}');
return sb.ToString();
}
public void SetGroupName(string group)
{
_groupName = group;
}
public bool ValidateKeys(out string error)
{
error = string.Empty;
var seen = new HashSet<string>(StringComparer.Ordinal);
foreach (var e in Entries)
{
if (string.IsNullOrWhiteSpace(e.Key))
{
error = "Empty key found.";
return false;
}
if (!seen.Add(e.Key))
{
error = $"Duplicate key: {e.Key}";
return false;
}
}
return true;
}
private static string Escape(string s)
{
if (string.IsNullOrEmpty(s)) return string.Empty;
var sb = new StringBuilder();
foreach (var c in s)
switch (c)
{
case '"': sb.Append("\\\""); break;
case '\\': sb.Append("\\\\"); break;
case '\n': sb.Append("\\n"); break;
case '\r': sb.Append("\\r"); break;
case '\t': sb.Append("\\t"); break;
default: sb.Append(c); break;
}
return sb.ToString();
}
[Serializable]
public class Entry
{
public string Key;
public ValueType Type = ValueType.String;
public string StringValue;
public double NumberValue;
public bool BoolValue;
public object GetValue()
{
return Type switch
{
ValueType.String => StringValue ?? string.Empty,
ValueType.Number => NumberValue,
ValueType.Boolean => BoolValue,
_ => string.Empty
};
}
}
}
}