207 lines
No EOL
6.9 KiB
C#
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
|
|
};
|
|
}
|
|
}
|
|
}
|
|
} |