// ####### // # # #### # # # # # #### // # # # # # # ## # # # // ##### # #### ###### # # # # # // # # # # # # # # # # ### // # # # # # # # # ## # # // # # #### # # # # # #### using System; using System.Threading.Tasks; using asap.core; using game; using UnityEngine; using UnityEngine.Networking; using Task = System.Threading.Tasks.Task; using UnityEngine.AddressableAssets; using System.Linq; using System.Reflection; using UnityEngine.ResourceManagement.AsyncOperations; using System.Collections.Generic; //using HybridCLR; using tysdk; using System.Collections; public class Boostrap : MonoBehaviour { [SerializeField] private RootCtx ctx; private IConfig config; [SerializeField] private BoostrapLoading bsLoading; [SerializeField] private SimpleDialog reconnectDialog; [SerializeField] private SimpleDialog updateDialog; [SerializeField] private SimpleDialog updateSugetDialog; [SerializeField] private SimpleDialog maintanceDialog; [SerializeField] private AgreementsPolicesPanel agreementPanel; [SerializeField] private AssetLabelReference preloadGroup; private System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch(); private double elapsed; void Start() { //This will override OS specific culture //settings and ensure predictable behavior when writing/reading string or text files. System.Globalization.CultureInfo.DefaultThreadCurrentCulture = new System.Globalization.CultureInfo("en-US"); ctx.OnInited += OnContextInited; bsLoading.Show(); bsLoading.SetProgress(0); bsLoading.SetDownloadSize(-1, -1); } // Callback from GContex when its ready private void OnContextInited() { config = GContext.container.Resolve(); Boost(); ctx.OnInited = null; } private async void Boost() { Boostrapi18n.Instance.Init(); bsLoading.SetInfo(Boostrapi18n.Instance.GetText("msg_initializing")); if(! await agreementPanel.Agreement()) { RootCtx.ExitGame(); return; } tysdk.UnityBridgeFunc.RequestATT(); if (!await CheckNetwork()) { await RetryDialog(reconnectDialog, CheckNetwork, "network"); } await Awaiters.NextFrame; // __ _ // _____ _____ _____ _____ ___ ___ _ __ / _(_) __ _ _____ _____ _____ //|_____|_____|_____|_____| / __/ _ \| '_ \| |_| |/ _` | |_____|_____|_____| //|_____|_____|_____|_____| | (_| (_) | | | | _| | (_| | |_____|_____|_____| // \___\___/|_| |_|_| |_|\__, | // |___/ #if AGG using (var e = GEvent.TackEvent("loading")) { e.AddContent("step", 1); e.AddContent("elapsed", 0); } #endif await Awaiters.NextFrame; sw.Start(); // if (!await LoadConfig()) // await RetryDialog(reconnectDialog, LoadConfig, "config"); var isSuccessLoadRemoteConfigFile = await LoadConfig(); sw.Stop(); elapsed = sw.Elapsed.TotalSeconds; sw.Reset(); #if AGG using (var e = GEvent.TackEvent("loading")) { e.AddContent("step", 2); e.AddContent("elapsed", elapsed); } #endif await Awaiters.NextFrame; var maintanceMsg = config.Get(GConstant.K_Maintance, string.Empty); if (!string.IsNullOrEmpty(maintanceMsg)) { ShowMantainceDialog(maintanceMsg); return; } await Awaiters.NextFrame; if (!await CheckVerion()) { return; } #if AGG // Change AGG server var aggserver = config.Get("AGG_SERVER", string.Empty); if (!string.IsNullOrEmpty(aggserver)) { //更新服务器 UnityBridgeFunc.UnityResetServerUrl(aggserver); } #endif await Awaiters.NextFrame; /* * * _ _ _ * _____ _____ _____ _____ (_)___ ___ _ __ __| | __ _| |_ __ _ _____ _____ _____ *|_____|_____|_____|_____| | / __|/ _ \| '_ \ / _` |/ _` | __/ _` | |_____|_____|_____| *|_____|_____|_____|_____| | \__ \ (_) | | | | | (_| | (_| | || (_| | |_____|_____|_____| * _/ |___/\___/|_| |_| \__,_|\__,_|\__\__,_| * |__/ */ #if !UNITY_EDITOR sw.Start(); // if (!await UpdateCfg(isSuccessLoadRemoteConfigFile)) // await RetryDialog(reconnectDialog, UpdateCfg, "cfg"); await UpdateCfg(isSuccessLoadRemoteConfigFile); sw.Stop(); elapsed = sw.Elapsed.TotalSeconds; sw.Reset(); #if AGG using (var e = GEvent.TackEvent("loading")) { e.AddContent("step", 3); e.AddContent("elapsed", elapsed); } #endif //AGG await Awaiters.NextFrame; #endif // !UNITY_EDITOR /* * _ _ _ * _____ _____ _____ _____ / \ __| | __| |_ __ _____ _____ _____ *|_____|_____|_____|_____| / _ \ / _` |/ _` | '__| |_____|_____|_____| *|_____|_____|_____|_____| / ___ \ (_| | (_| | | |_____|_____|_____| * /_/ \_\__,_|\__,_|_| */ /* sw.Start(); */ /**/ /* Addressables.WebRequestOverride = (UnityWebRequest request) => request.timeout = 15; */ /**/ /* while(!await UpdateAsset()) */ /* { */ /* #if AGG */ /* using (var e = GEvent.TackEvent("retry")) */ /* { */ /* e.AddContent("type", "update"); */ /* } */ /* #endif */ /* Debug.LogWarning("UpdateAsset failed"); */ /* } */ /**/ /* sw.Stop(); */ /* elapsed = sw.Elapsed.TotalSeconds; */ /* sw.Reset(); */ /**/ /* #if AGG */ /* using (var e = GEvent.TackEvent("loading")) */ /* { */ /* e.AddContent("step", 5); */ /* e.AddContent("elapsed", elapsed); */ /* } */ /* #endif */ /* sw.Start(); await AfterAssetUpdate(); sw.Stop(); elapsed = sw.Elapsed.TotalSeconds; sw.Reset(); */ /* * _ * _____ _____ _____ _____ ___ ___| |_ _ _ _ __ _____ _____ _____ *|_____|_____|_____|_____| / __|/ _ \ __| | | | '_ \ |_____|_____|_____| *|_____|_____|_____|_____| \__ \ __/ |_| |_| | |_) | |_____|_____|_____| * |___/\___|\__|\__,_| .__/ * |_| */ await AfterAssetUpdateV2(); #if AGG using (var e = GEvent.TackEvent("loading")) { e.AddContent("step", 6); e.AddContent("elapsed", elapsed); } #endif //StopUpdateLoadingInfo(); await GoToStageV2(); } private async Task CheckNetwork() { await Awaiters.NextFrame; return Application.internetReachability != NetworkReachability.NotReachable; } private async Task LoadConfig() { var success = await config.InitConfig(); // Change Addressable remote location, using custom assetbundle server var customAddressableRemoteURL = config.Get(GConstant.K_Custom_Addressable, string.Empty); var originAddressableRemoteURL = config.Get(GConstant.K_Origin_Addressable, string.Empty); if (!string.IsNullOrEmpty(customAddressableRemoteURL) && !string.IsNullOrEmpty(originAddressableRemoteURL)) { ctx.ChangeAddressableRemoteUrl(customAddressableRemoteURL, originAddressableRemoteURL); } await Awaiters.NextFrame; bsLoading.SetProgress(0.02f); Debug.Log($"[Boostrap]::LoadConfig Done success: {success}"); return success; } private async Task UpdateCfg(bool isSuccessLoadRemoteConfigFile) { var result = await cfg.Tables.UpdateCfg(config, isSuccessLoadRemoteConfigFile); bsLoading.SetProgress(0.05f); return result; } private async Task CheckVerion() { var remoteVer = VersionTool.FromString(config.Get(GConstant.K_Ver, string.Empty)); var localVer = VersionTool.FromLocal(); if (remoteVer > localVer) { updateDialog.Config(new SimpleDialog.Context() { message = Boostrapi18n.Instance.GetText("title_hardupdate"), message2 = Boostrapi18n.Instance.GetText("content_hardupdate"), okAction = () => { #if UNITY_IOS var url = config.Get(GConstant.K_APP_Store_URL, string.Empty); #else var url = config.Get(GConstant.K_Google_Play_URL, string.Empty); #endif Application.OpenURL(url); RootCtx.ExitGame(); } }); return false; } var newVer = config.Get(GConstant.K_Ver_New, string.Empty); if (!string.IsNullOrEmpty(newVer)) { var confirm = new TaskCompletionSource(); updateSugetDialog.Config(new SimpleDialog.Context() { message = Boostrapi18n.Instance.GetText("title_hardupdate"), message2 = Boostrapi18n.Instance.GetText("content_hardupdate"), okAction = () => { #if UNITY_IOS var url = config.Get(GConstant.K_APP_Store_URL, string.Empty); #else var url = config.Get(GConstant.K_Google_Play_URL, string.Empty); #endif Application.OpenURL(url); confirm.SetResult(false); RootCtx.ExitGame(); }, closeAction = () => { confirm.SetResult(true); } }); return await confirm.Task; } return true; } private async Task UpdateAsset() { #if UNITY_EDITOR await Awaiters.NextFrame; return true; #else await Awaiters.NextFrame; bsLoading.SetVersionInfo(VersionTool.FromLocal().ToVerString()); try { Debug.Log("Start UpdateAsset"); //Addressables.WebRequestOverride = (UnityWebRequest request) => request.timeout = 6; List update_catalogs = null; var updateCheckHandle = Addressables.CheckForCatalogUpdates(false); update_catalogs = await updateCheckHandle.Task; if(updateCheckHandle.OperationException != null || updateCheckHandle.Status == AsyncOperationStatus.Failed) { Debug.LogError($"update checked failed: { updateCheckHandle.OperationException.Message }"); return false; } Addressables.Release(updateCheckHandle); if(null != update_catalogs) { Debug.Log($"update catalogs count: {update_catalogs.Count}"); } if(null != update_catalogs && update_catalogs.Count > 0) { var updateCatalogHandle = Addressables.UpdateCatalogs(update_catalogs, false); await updateCatalogHandle.Task; if(updateCatalogHandle.OperationException != null || updateCatalogHandle.Status == AsyncOperationStatus.Failed) { Debug.LogError($"update catalogs failed: {updateCatalogHandle.OperationException.Message}"); return false; } Addressables.Release(updateCatalogHandle); } var locationsHandle = Addressables.LoadResourceLocationsAsync(preloadGroup.labelString); var locations = await locationsHandle.Task; if(locationsHandle.OperationException != null || locationsHandle.Status == AsyncOperationStatus.Failed) { Debug.LogError($"load resource locations failed: {locationsHandle.OperationException.Message}"); return false; } Addressables.Release(locationsHandle); var downloadSizeHandle = Addressables.GetDownloadSizeAsync(locations); var downloadSize = await downloadSizeHandle.Task; if(downloadSizeHandle.OperationException != null || downloadSizeHandle.Status == AsyncOperationStatus.Failed) { Debug.LogError($"get download size failed: {downloadSizeHandle.OperationException.Message}"); return false; } Addressables.Release(downloadSizeHandle); if(downloadSize > 0) { bsLoading.SetInfo(Boostrapi18n.Instance.GetText("msg_downloading")); var downloadHandle = Addressables.DownloadDependenciesAsync(locations); DownloadStatus downloadStatus; long downloadedBytes = 0; float percent = 0; while (!downloadHandle.IsDone && downloadHandle.OperationException == null && downloadHandle.Status != AsyncOperationStatus.Failed) { downloadStatus = downloadHandle.GetDownloadStatus(); if(downloadStatus.DownloadedBytes > downloadedBytes) downloadedBytes = downloadStatus.DownloadedBytes; if(downloadStatus.Percent > percent) percent = downloadStatus.Percent; bsLoading.SetProgress(percent); bsLoading.SetInfo(Boostrapi18n.Instance.GetText("msg_downloading")); bsLoading.SetDownloadSize(downloadedBytes, downloadStatus.TotalBytes); //bsLoading.SetSubProgress(downloadHandle.PercentComplete); await Awaiters.NextFrame; } if(downloadHandle.OperationException != null) { Debug.LogError($"Download Fail: {downloadHandle.OperationException.Message}"); return false; } if(downloadHandle.Status == AsyncOperationStatus.Failed) { Debug.LogError($"Download Fail Status: {downloadHandle.Status}"); return false; } downloadStatus = downloadHandle.GetDownloadStatus(); if(downloadStatus.TotalBytes == 0 && downloadStatus.DownloadedBytes == 0) { Debug.LogError($"Download Fail: {downloadStatus.TotalBytes} {downloadStatus.DownloadedBytes}"); return false; } Addressables.Release(downloadHandle); } //bsLoading.SetSubProgress(-1); } catch(System.Exception e) { Debug.LogError($"[Boostrap]::UpdateAsset error: {e.Message}"); return false; } //finally //{ //Addressables.WebRequestOverride = null; //} bsLoading.SetProgress(0.1f); bsLoading.SetInfo(Boostrapi18n.Instance.GetText("msg_loadingresource")); await Awaiters.NextFrame; var remoteVer = VersionTool.FromString(config.Get(GConstant.K_Ver, string.Empty)); remoteVer.Save(); bsLoading.SetVersionInfo(remoteVer.ToVerString()); await Awaiters.NextFrame; return true; #endif } private async Task AfterAssetUpdateV2() { bsLoading.SetProgress(0.15f); await SubBoostrap.RegisterServices(ctx.gameObject); bsLoading.SetProgress(0.16f); await SubBoostrap.AfterAssetUpdate(config, bsLoading.SetProgress); bsLoading.SetProgress(0.18f); } private async Task GoToStageV2() { Debug.Log("Boostrap Done"); await SubBoostrap.ShowLoadingScreen(config, ctx.gameObject); await Awaiters.NextFrame; bsLoading.Hide(); _ = GContext.container.Resolve().SwitchStageAsync("LoginStage"); } /* private Type _hotUpdateBoostrapType; private async Task AfterAssetUpdate() { var hotUpdateAsset = await LoadDll(); bsLoading.SetProgress(0.15f); Debug.Log($"[Boostrap]::AfterAssetUpdate LoadDll Done"); _hotUpdateBoostrapType = hotUpdateAsset.GetType("HotUpateBoostrap"); var task = (Task)_hotUpdateBoostrapType.GetMethod("RegisterServices").Invoke(null, new object[] { ctx.gameObject }); await task; bsLoading.SetProgress(0.16f); var progressAction = Delegate.CreateDelegate(typeof(Action), bsLoading, "SetProgress"); task = (Task)_hotUpdateBoostrapType.GetMethod("AfterAssetUpdate").Invoke(null, new object[] { config, progressAction }); await task; Debug.Log($"[Boostrap]::AfterAssetUpdate RegisterServices Done"); bsLoading.SetProgress(0.18f); } private async Task LoadDll() { #if !UNITY_EDITOR //Load HotUpdate dll var dllAsset = await Addressables.LoadAssetAsync("Hotupdate.dll").Task; var bytes = dllAsset.bytes; var hotUpdateAsset = Assembly.Load(bytes); Addressables.Release(dllAsset); //Load Aot dll var aotDllTasks = GConstant.AOT_Dlls.Select(aotDllName => Addressables.LoadAssetAsync(aotDllName).Task); await Task.WhenAll(aotDllTasks); var dllLoadTasks = new List(); foreach (var aotDllTask in aotDllTasks) { var aotDllBytes = aotDllTask.Result.bytes; dllLoadTasks.Add(Task.Run(() => { HybridCLR.RuntimeApi.LoadMetadataForAOTAssembly(aotDllBytes, HomologousImageMode.SuperSet); })); } await Task.WhenAll(dllLoadTasks); #else var hotUpdateAsset = System.AppDomain.CurrentDomain.GetAssemblies().First(a => a.GetName().Name == "HotUpdate"); await Awaiters.NextFrame; #endif return hotUpdateAsset; } private async Task GoToStage() { Debug.Log("Boostrap Done"); var task = (Task)_hotUpdateBoostrapType.GetMethod("ShowLoadingScreen").Invoke(null, new object[] { config }); await task; await Awaiters.NextFrame; bsLoading.Hide(); _ = GContext.container.Resolve().SwitchStageAsync("LoginStage"); } */ void OnDestory() { ctx.OnInited -= OnContextInited; } private void ShowMantainceDialog(string maintanceMessage) { maintanceDialog.Config(new SimpleDialog.Context() { message = Boostrapi18n.Instance.GetText("title_maintenance"), message2 = string.Format(Boostrapi18n.Instance.GetText("content_maintenance"), maintanceMessage), okAction = () => RootCtx.ExitGame(), }); } private async Task RetryDialog(SimpleDialog dialog, Func> func, string retryType) { while (true) { #if AGG using (var e = GEvent.TackEvent("retry")) { e.AddContent("type", retryType); } #endif var retry = new TaskCompletionSource(); dialog.Config(new SimpleDialog.Context() { message = Boostrapi18n.Instance.GetText("subtitle_networkfailed"), message2 = Boostrapi18n.Instance.GetText("content_networkfailed"), okAction = async () => retry.SetResult(await func()), }); if (await retry.Task) return; } } }