using System; using System.Collections.Generic; using System.IO; 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 = "{\"StaticResURL\":\"https://sls-cloudfunction-ap-beijing-code-1305151099.cos.ap-beijing.myqcloud.com/mini/0_47\",\"PlayFabTitle\":\"108EE8\",\"Origin_Addressable_URL\":\"https://sls-cloudfunction-ap-beijing-code-1305151099.cos.ap-beijing.myqcloud.com/mini/0_47/Android\",\"HeartBeat\":300,\"CustomID\":\"{{input}}\",\"GM\":\"1\",\"FuncUrl\":\"http://62.234.191.215:7071\",\"FuncKey\":\"testKey\",\"Ver\":\"0.47.2f4aa9f\"}"; 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) { var staticResURL = GConstant.V_Static_Res_URL; if (ChannelManager.IsRustoreStore) staticResURL = GConstant.V_Static_Res_URL_RU; RemoteConfigUrl = string.Format(staticResURL, VersionTool.PackVer); } JSONNode remoteConfig = await GetRemoteConfig(); if (remoteConfig == null) { remoteConfig = JSON.Parse(defaultconfig); 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 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); * } */ 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; } }