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 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 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($"Load remote config success {retryTime + 1}/{maxRetryTimes}"); return JSON.Parse(rawConfig); } } return null; } private static async Task 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 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 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($"Debug Static Res URL => {RemoteConfigUrl}"); } 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($"Debug Static Res URL => {RemoteConfigUrl}"); } return debugConfig; } return null; } }