373 lines
14 KiB
C#
373 lines
14 KiB
C#
using System;
|
||
using System.Collections.Generic;
|
||
using System.IO;
|
||
using System.Security.Cryptography;
|
||
using System.Text;
|
||
using System.Threading;
|
||
using System.Threading.Tasks;
|
||
using asap.core;
|
||
using SimpleJSON;
|
||
using UnityEngine;
|
||
using UnityEngine.Networking;
|
||
|
||
public static class ConfigExtension
|
||
{
|
||
private const string defaultconfig = "9Nf/P8vm2+7Ciph2jH1QnEaHCK3cAl7lNnCJc/uxSKGVqcJgWilN3VgaC/74coHRiRpxEelC2+ia7qcjnHhA3OQN82rcVJOP24pGKrSbSgOIGbH9cCEUhdYRo5vnVgLBqlTS/c/AajowTkN59O0AYxDC7J3jq6ptLXdmuG27JYS9NXOQDkgxpzEpG7JTtczHKxhWAv+9uSzbJRCs16KeusbJX1YtNZM8wjCD9esTv4zXXMIpZPlXb86NjHiI02TakAdHo0plO+Di9n5UCC8/4+QGoKNyQgtvIkzMyoGziO4DOLy/VkfITenFv/+9DY0LmpZGa7Cog89znaYyVMTiC/3R2IefXXh04+8uqmyzeVKqh0grcU9qmz485wCoUl/TM9F8+s1Jhp/eaB/E7luOjc57Kz0Eyw2Qko7Fo+lCaRJE1uex9fB46v0LhddSHCEWw8QERmqG9KcmvG4LsyafupA8kliDzj7a7XIzGrgnNnsMxLgeYsSbNiBvXXESeeNCoqiOQTTBpk/br0QuTHTG2DRxUZZ4KM3SKZtHEZ2bZV2FUS4azFpfDXUGoc86O+xz6hAIMbdKCHyqyEeKMmHIzQI9OlIaa3KGwk+w1eFDUuq4zyTh+qGuClZynanUkfNe8mTmFtBax2f3MJHjGpXJT0VdtY/R+5ZQO0nCU30THfnryG0GURsrv6MQYiQX5hsLqG9Z72ybbbbqIR2rvE2w13DM/tqTb4i/hN0+pbeTKkUpdNIWEsR94Q==";
|
||
|
||
private static string RemoteConfigUrl { get; set; }
|
||
|
||
public static void Rest(this IConfig config)
|
||
{
|
||
RemoteConfigUrl = null;
|
||
}
|
||
|
||
public static async Task<bool> InitConfig(this IConfig config)
|
||
{
|
||
JSONNode streamingAssetConfig = null;
|
||
JSONNode persistConfig = null;
|
||
bool isSuccessLoadRemoteConfigFile = true;
|
||
|
||
if (RemoteConfigUrl == null)
|
||
{
|
||
streamingAssetConfig = GetStreamingAssetConfig();
|
||
persistConfig = GetPersistConfig();
|
||
}
|
||
|
||
if (RemoteConfigUrl == null)
|
||
{
|
||
RemoteConfigUrl = string.Format(GConstant.V_Static_Res_URL, VersionTool.PackVer);
|
||
}
|
||
|
||
JSONNode remoteConfig = await GetRemoteConfig();
|
||
|
||
if (remoteConfig == null)
|
||
{
|
||
var remoteConfigStr = Decrypt(defaultconfig);
|
||
remoteConfig = JSON.Parse(remoteConfigStr);
|
||
remoteConfig["Ver"] = VersionTool.FromLocal().ToVerString();
|
||
isSuccessLoadRemoteConfigFile = false;
|
||
}
|
||
|
||
foreach (var element in remoteConfig)
|
||
{
|
||
config.Set(element.Key, element.Value.Value);
|
||
}
|
||
|
||
if (streamingAssetConfig != null)
|
||
{
|
||
foreach (var element in streamingAssetConfig)
|
||
{
|
||
config.Set(element.Key, element.Value.Value);
|
||
}
|
||
}
|
||
|
||
if (persistConfig != null)
|
||
{
|
||
foreach (var element in persistConfig)
|
||
{
|
||
config.Set(element.Key, element.Value.Value);
|
||
}
|
||
}
|
||
|
||
config.Set(GConstant.K_Static_Res_URL, RemoteConfigUrl);
|
||
|
||
return isSuccessLoadRemoteConfigFile;
|
||
}
|
||
|
||
|
||
private static readonly float[] timeoutValues = new float[] { 1, 2, 2 };
|
||
|
||
public static async Task<JSONNode> GetRemoteConfig()
|
||
{
|
||
UnityEngine.Assertions.Assert.IsNotNull(RemoteConfigUrl, "Config is not inited");
|
||
|
||
await Awaiters.NextFrame;
|
||
|
||
var maxRetryTimes = timeoutValues.Length;
|
||
|
||
for (int retryTime = 0; retryTime < maxRetryTimes; retryTime++)
|
||
{
|
||
var rawConfig = await DoGetRawConfigUWR(timeoutValues[retryTime], retryTime, maxRetryTimes);
|
||
if (!string.IsNullOrEmpty(rawConfig))
|
||
{
|
||
Debug.Log($"<color=orange>Load remote config success {retryTime + 1}/{maxRetryTimes}</color>");
|
||
return JSON.Parse(rawConfig);
|
||
}
|
||
}
|
||
|
||
return null;
|
||
}
|
||
|
||
private static async Task<string> DoGetRawConfigUWR(float timeoutInSec, int retryTimes, int maxRetryTimes)
|
||
{
|
||
using (var req = UnityWebRequest.Get($"{RemoteConfigUrl}/{GConstant.V_Remote_Config_Path}"))
|
||
{
|
||
var stopwatch = new System.Diagnostics.Stopwatch();
|
||
var cts = new CancellationTokenSource();
|
||
cts.CancelAfter(TimeSpan.FromSeconds(timeoutInSec));
|
||
var ct = cts.Token;
|
||
stopwatch.Start();
|
||
req.timeout = (int)timeoutInSec;
|
||
req.SetRequestHeader("Accept", "application/json");
|
||
var op = req.SendWebRequest();
|
||
try
|
||
{
|
||
while (!op.isDone && !ct.IsCancellationRequested)
|
||
{
|
||
await Awaiters.NextFrame;
|
||
}
|
||
ct.ThrowIfCancellationRequested();
|
||
stopwatch.Stop();
|
||
|
||
if (req.result == UnityWebRequest.Result.Success)
|
||
{
|
||
return Decrypt(req.downloadHandler.text);
|
||
}
|
||
|
||
#if AGG
|
||
using (var e = GEvent.TackEvent("retry"))
|
||
{
|
||
e.AddContent("type", "config");
|
||
e.AddContent("duration_ms", stopwatch.ElapsedMilliseconds);
|
||
e.AddContent("reason", req.result.ToString());
|
||
e.AddContent("skip", retryTimes >= maxRetryTimes - 1);
|
||
e.AddContent("error", req.error);
|
||
}
|
||
#endif // AGG
|
||
|
||
Debug.LogWarning($"Load remote config failed {req.result.ToString()} {req.error} {retryTimes + 1}/{maxRetryTimes}");
|
||
|
||
}
|
||
catch (OperationCanceledException)
|
||
{
|
||
req.Abort();
|
||
stopwatch.Stop();
|
||
|
||
#if AGG
|
||
using (var e = GEvent.TackEvent("retry"))
|
||
{
|
||
e.AddContent("type", "config");
|
||
e.AddContent("duration_ms", stopwatch.ElapsedMilliseconds);
|
||
e.AddContent("reason", "cancel");
|
||
e.AddContent("skip", retryTimes >= maxRetryTimes - 1);
|
||
e.AddContent("error", req.error ?? "cancel");
|
||
}
|
||
#endif // AGG
|
||
|
||
Debug.LogWarning($"Load remote config cancel {req.result.ToString()} {req.error} {retryTimes + 1}/{maxRetryTimes}");
|
||
|
||
}
|
||
finally
|
||
{
|
||
cts.Dispose();
|
||
}
|
||
|
||
return null;
|
||
}
|
||
}
|
||
|
||
|
||
/**
|
||
private static async Task<string> DoGetRawConfig(float timeoutInSec, int retryTimes)
|
||
{
|
||
var timeout = TimeSpan.FromSeconds(timeoutInSec);
|
||
var handler = new System.Net.Http.HttpClientHandler(){
|
||
SslProtocols = System.Security.Authentication.SslProtocols.Tls12 | System.Security.Authentication.SslProtocols.Tls11 | System.Security.Authentication.SslProtocols.Tls,
|
||
ServerCertificateCustomValidationCallback = (sender, cert, chain, sslPolicyErrors) => { return true; }
|
||
};
|
||
using (var httpClient = new System.Net.Http.HttpClient(handler))
|
||
{
|
||
httpClient.Timeout = timeout;
|
||
var stopwatch = new System.Diagnostics.Stopwatch();
|
||
|
||
try
|
||
{
|
||
stopwatch.Start();
|
||
var respon = await httpClient.GetAsync($"{RemoteConfigUrl}/{GConstant.V_Remote_Config_Path}");
|
||
stopwatch.Stop();
|
||
|
||
if (respon.IsSuccessStatusCode)
|
||
{
|
||
string remoteConfigStr = await respon.Content.ReadAsStringAsync();
|
||
await Awaiters.NextFrame;
|
||
return Decrypt(remoteConfigStr);
|
||
}
|
||
else
|
||
{
|
||
var elapsed = stopwatch.ElapsedMilliseconds;
|
||
Debug.LogWarning($"Load remote config failed {respon.StatusCode.ToString()}");
|
||
#if AGG
|
||
using (var e = GEvent.TackEvent("retry"))
|
||
{
|
||
e.AddContent("type", "config");
|
||
e.AddContent("duration_ms", elapsed);
|
||
e.AddContent("reason", respon.StatusCode.ToString());
|
||
e.AddContent("skip", retryTimes >= 2);
|
||
e.AddContent("error", "http_request_failed");
|
||
}
|
||
#endif//AGG
|
||
}
|
||
}
|
||
catch (System.Net.Http.HttpRequestException httpEx)
|
||
{
|
||
if (stopwatch.IsRunning)
|
||
stopwatch.Stop();
|
||
var elapsed = stopwatch.Elapsed;
|
||
|
||
var errorMessage = $"HTTP request failed: {httpEx.Message}";
|
||
if (httpEx.InnerException != null)
|
||
{
|
||
errorMessage += $" Inner: {httpEx.InnerException.Message}";
|
||
if(httpEx.InnerException.InnerException != null)
|
||
{
|
||
errorMessage += $" InnerInner: {httpEx.InnerException.InnerException.Message}";
|
||
}
|
||
}
|
||
Debug.LogWarning(errorMessage);
|
||
|
||
#if AGG
|
||
using (var e = GEvent.TackEvent("retry"))
|
||
{
|
||
e.AddContent("type", "config");
|
||
e.AddContent("duration_ms", elapsed.TotalMilliseconds);
|
||
e.AddContent("reason", "httpEx");
|
||
e.AddContent("skip", retryTimes >= 2);
|
||
e.AddContent("error", errorMessage);
|
||
}
|
||
#endif//AGG
|
||
}
|
||
catch (System.Threading.Tasks.TaskCanceledException timeoutEx)
|
||
{
|
||
if (stopwatch.IsRunning)
|
||
stopwatch.Stop();
|
||
var elapsed = stopwatch.Elapsed;
|
||
|
||
Debug.LogWarning($"Request timeout after {timeoutInSec}s: {timeoutEx.Message}");
|
||
|
||
#if AGG
|
||
using (var e = GEvent.TackEvent("retry"))
|
||
{
|
||
e.AddContent("type", "config");
|
||
e.AddContent("duration_ms", elapsed.TotalMilliseconds);
|
||
e.AddContent("reason", $"timeout");
|
||
e.AddContent("skip", retryTimes >= 2);
|
||
e.AddContent("error", timeoutEx.Message);
|
||
}
|
||
#endif//AGG
|
||
}
|
||
catch (System.Security.Cryptography.CryptographicException cryptoEx)
|
||
{
|
||
if (stopwatch.IsRunning)
|
||
stopwatch.Stop();
|
||
var elapsed = stopwatch.Elapsed;
|
||
|
||
Debug.LogError($"Decryption failed: {cryptoEx.Message}");
|
||
|
||
#if AGG
|
||
using (var e = GEvent.TackEvent("retry"))
|
||
{
|
||
e.AddContent("type", "config");
|
||
e.AddContent("duration_ms", elapsed.TotalMilliseconds);
|
||
e.AddContent("reason", "cryptoEx");
|
||
e.AddContent("skip", retryTimes >= 2);
|
||
e.AddContent("error", cryptoEx.Message);
|
||
}
|
||
#endif//AGG
|
||
}
|
||
catch (Exception exception)
|
||
{
|
||
if (stopwatch.IsRunning)
|
||
stopwatch.Stop();
|
||
var elapsed = stopwatch.ElapsedMilliseconds;
|
||
|
||
var errorMessage = $"Unexpected error loading remote config: {exception.Message}";
|
||
if (exception.InnerException != null)
|
||
{
|
||
errorMessage += $" Inner: {exception.InnerException.Message}";
|
||
}
|
||
Debug.LogError(errorMessage);
|
||
|
||
#if AGG
|
||
using (var e = GEvent.TackEvent("retry"))
|
||
{
|
||
e.AddContent("type", "config");
|
||
e.AddContent("duration_ms", elapsed);
|
||
e.AddContent("reason", "unexpected_error");
|
||
e.AddContent("skip", retryTimes >= 2);
|
||
e.AddContent("error", errorMessage);
|
||
}
|
||
#endif//AGG
|
||
}
|
||
}
|
||
return null;
|
||
}
|
||
*/
|
||
|
||
/*
|
||
* public static async Task<JSONNode> GetRemoteConfig()
|
||
* {
|
||
* UnityEngine.Assertions.Assert.IsNotNull(RemoteConfigUrl, "Config is not inited");
|
||
* var request = UnityWebRequest.Get($"{RemoteConfigUrl}/{GConstant.V_Remote_Config_Path}");
|
||
* request.timeout = 5;
|
||
* await request.SendWebRequest();
|
||
*
|
||
* if (request.result != UnityWebRequest.Result.Success)
|
||
* {
|
||
* return null;
|
||
* }
|
||
*
|
||
* string remoteConfigStr = request.downloadHandler.text;
|
||
* remoteConfigStr = Decrypt(remoteConfigStr);
|
||
* return SimpleJSON.JSON.Parse(remoteConfigStr);
|
||
* }
|
||
*/
|
||
|
||
const string RTMKey = "tbambooz";
|
||
private static string Decrypt(string text)
|
||
{
|
||
DESCryptoServiceProvider des = new DESCryptoServiceProvider();
|
||
des.Key = Encoding.ASCII.GetBytes(RTMKey);
|
||
des.IV = Encoding.ASCII.GetBytes(RTMKey);
|
||
|
||
ICryptoTransform decryptor = des.CreateDecryptor(des.Key, des.IV);
|
||
byte[] inputBytes = Convert.FromBase64String(text);
|
||
byte[] outputBytes = decryptor.TransformFinalBlock(inputBytes, 0, inputBytes.Length);
|
||
|
||
return Encoding.UTF8.GetString(outputBytes);
|
||
}
|
||
|
||
private static JSONNode GetStreamingAssetConfig()
|
||
{
|
||
var debugConfigPath = GConstant.V_Debug_Config_Path;
|
||
var text = BetterStreamingAssets.ReadAllText(debugConfigPath);
|
||
//如果第一个字符是#e,就返回false
|
||
if (!text.StartsWith("#"))
|
||
{
|
||
var debugConfig = SimpleJSON.JSON.Parse(text);
|
||
if (debugConfig.HasKey(GConstant.K_Static_Res_URL))
|
||
{
|
||
RemoteConfigUrl = debugConfig[GConstant.K_Static_Res_URL];
|
||
Debug.Log($"<color=yellow>Debug Static Res URL => {RemoteConfigUrl}</color>");
|
||
}
|
||
return debugConfig;
|
||
}
|
||
return null;
|
||
}
|
||
|
||
private static JSONNode GetPersistConfig()
|
||
{
|
||
var debugConfigPath = Path.Combine(Application.persistentDataPath, GConstant.V_Debug_Config_Path);
|
||
if (File.Exists(debugConfigPath))
|
||
{
|
||
var debugConfig = SimpleJSON.JSON.Parse(File.ReadAllText(debugConfigPath));
|
||
if (debugConfig.HasKey(GConstant.K_Static_Res_URL))
|
||
{
|
||
RemoteConfigUrl = debugConfig[GConstant.K_Static_Res_URL];
|
||
Debug.Log($"<color=yellow>Debug Static Res URL => {RemoteConfigUrl}</color>");
|
||
}
|
||
return debugConfig;
|
||
}
|
||
return null;
|
||
}
|
||
}
|