Files
2026-05-26 16:15:54 +08:00

616 lines
20 KiB
C#

// #######
// # # #### # # # # # ####
// # # # # # # ## # # #
// ##### # #### ###### # # # # #
// # # # # # # # # # # ###
// # # # # # # # # ## # #
// # # #### # # # # # ####
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<IConfig>();
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<bool> CheckNetwork()
{
await Awaiters.NextFrame;
return Application.internetReachability != NetworkReachability.NotReachable;
}
private async Task<bool> 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<bool> UpdateCfg(bool isSuccessLoadRemoteConfigFile)
{
var result = await cfg.Tables.UpdateCfg(config, isSuccessLoadRemoteConfigFile);
bsLoading.SetProgress(0.05f);
return result;
}
private async Task<bool> 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<bool>();
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<bool> 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<string> 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<IStageService>().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<float>), 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<Assembly> LoadDll()
{
#if !UNITY_EDITOR
//Load HotUpdate dll
var dllAsset = await Addressables.LoadAssetAsync<TextAsset>("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<TextAsset>(aotDllName).Task);
await Task.WhenAll(aotDllTasks);
var dllLoadTasks = new List<Task>();
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<IStageService>().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<Task<bool>> func, string retryType)
{
while (true)
{
#if AGG
using (var e = GEvent.TackEvent("retry"))
{
e.AddContent("type", retryType);
}
#endif
var retry = new TaskCompletionSource<bool>();
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;
}
}
}