| | |
| | | private bool isOpeningLaunchStaticWin = false;
|
| | | private bool isBackgroundUpdateStarted = false;
|
| | |
|
| | | private static void LogTiming(string phase, float startTime)
|
| | | private struct LaunchTraceInfo
|
| | | {
|
| | | Debug.LogError($"[Launch][Timing] {phase} cost={(Time.realtimeSinceStartup - startTime):F3}s");
|
| | | public string part;
|
| | | public DateTime startLocalTime;
|
| | | public float startRealtime;
|
| | | }
|
| | |
|
| | | private static readonly Dictionary<string, LaunchTraceInfo> RunningTraces = new Dictionary<string, LaunchTraceInfo>(32);
|
| | |
|
| | | private static void TraceBegin(string key, string part)
|
| | | {
|
| | | var now = DateTime.Now;
|
| | | RunningTraces[key] = new LaunchTraceInfo
|
| | | {
|
| | | part = string.IsNullOrEmpty(part) ? "未分组" : part,
|
| | | startLocalTime = now,
|
| | | startRealtime = Time.realtimeSinceStartup
|
| | | };
|
| | |
|
| | | Debug.LogError($"[启动时序][{RunningTraces[key].part}][Launch.{key}] 开始时间: {FormatTraceTime(now)}");
|
| | | }
|
| | |
|
| | | private static void TraceEnd(string key, string part = null, string suffix = null)
|
| | | {
|
| | | var now = DateTime.Now;
|
| | | if (!RunningTraces.TryGetValue(key, out LaunchTraceInfo traceInfo))
|
| | | {
|
| | | var fallbackPart = string.IsNullOrEmpty(part) ? "未分组" : part;
|
| | | var fallbackSuffix = string.IsNullOrEmpty(suffix) ? string.Empty : $"; {suffix}";
|
| | | Debug.LogError($"[启动时序][{fallbackPart}][Launch.{key}] 结束时间: {FormatTraceTime(now)}; 总计花费: 未知(缺少开始记录){fallbackSuffix}");
|
| | | return;
|
| | | }
|
| | |
|
| | | RunningTraces.Remove(key);
|
| | |
|
| | | var elapsedMs = (Time.realtimeSinceStartup - traceInfo.startRealtime) * 1000f;
|
| | | var resolvedPart = string.IsNullOrEmpty(part) ? traceInfo.part : part;
|
| | | var resolvedSuffix = string.IsNullOrEmpty(suffix) ? string.Empty : $"; {suffix}";
|
| | | Debug.LogError($"[启动时序][{resolvedPart}][Launch.{key}] 开始时间: {FormatTraceTime(traceInfo.startLocalTime)}; 结束时间: {FormatTraceTime(now)}; 总计花费: {elapsedMs:F1}ms{resolvedSuffix}");
|
| | | }
|
| | |
|
| | | private static string FormatTraceTime(DateTime dateTime)
|
| | | {
|
| | | return dateTime.ToString("HH:mm:ss.fff");
|
| | | }
|
| | |
|
| | | private static Launch m_Instance;
|
| | | private void Awake()
|
| | | {
|
| | | float startTime = Time.realtimeSinceStartup;
|
| | | TraceBegin("Awake", "冷启动入口");
|
| | | if (m_Instance != null)
|
| | | {
|
| | | Debug.LogError("Launch Instance is not null");
|
| | | TraceEnd("Awake", "冷启动入口", "结果: 已存在实例");
|
| | | return;
|
| | | }
|
| | |
|
| | |
| | | #endif
|
| | |
|
| | | OpenLaunchStaticWin().Forget();
|
| | | LogTiming("Awake", startTime);
|
| | | TraceEnd("Awake", "冷启动入口");
|
| | | }
|
| | |
|
| | | private async UniTaskVoid OpenLaunchStaticWin()
|
| | |
| | | if (isOpeningLaunchStaticWin || launchStaticWin != null)
|
| | | return;
|
| | |
|
| | | TraceBegin("OpenLaunchStaticWin", "启动UI静态层");
|
| | | isOpeningLaunchStaticWin = true;
|
| | | try
|
| | | {
|
| | |
| | | finally
|
| | | {
|
| | | isOpeningLaunchStaticWin = false;
|
| | | TraceEnd("OpenLaunchStaticWin", "启动UI静态层");
|
| | | }
|
| | | }
|
| | |
|
| | |
| | |
|
| | | private async UniTaskVoid UpdateBackgroundAsync()
|
| | | {
|
| | | TraceBegin("UpdateBackgroundAsync", "启动背景更新");
|
| | | try
|
| | | {
|
| | | #if UNITY_EDITOR
|
| | | TraceEnd("UpdateBackgroundAsync", "启动背景更新", "结果: Editor 下跳过");
|
| | | return;
|
| | | #endif
|
| | | float elapsed = 0f;
|
| | |
| | | if (elapsed > 30f)
|
| | | {
|
| | | Debug.LogError("[Launch] UpdateBackground give up: cdnUrl not ready within 30s.");
|
| | | TraceEnd("UpdateBackgroundAsync", "启动背景更新", "结果: cdnUrl 超时");
|
| | | return;
|
| | | }
|
| | | }
|
| | |
| | | if (versionReq.result != UnityEngine.Networking.UnityWebRequest.Result.Success)
|
| | | {
|
| | | Debug.LogError($"[Launch] Fetch version file failed: {versionReq.error}");
|
| | | TraceEnd("UpdateBackgroundAsync", "启动背景更新", "结果: 版本文件拉取失败");
|
| | | return;
|
| | | }
|
| | |
|
| | |
| | | if (imgReq.result != UnityEngine.Networking.UnityWebRequest.Result.Success)
|
| | | {
|
| | | Debug.LogError($"[Launch] Download background image failed: {imgReq.error}");
|
| | | TraceEnd("UpdateBackgroundAsync", "启动背景更新", "结果: 背景图下载失败");
|
| | | return;
|
| | | }
|
| | |
|
| | |
| | | File.WriteAllBytes(dir + "LoginBackground_" + newVersion + ".jpg", imgReq.downloadHandler.data);
|
| | | LocalSave.SetString("bg_version", newVersion);
|
| | | Debug.LogError($"[Launch] Background updated to version {newVersion}");
|
| | | TraceEnd("UpdateBackgroundAsync", "启动背景更新", $"结果: 背景更新到 {newVersion}");
|
| | | }
|
| | | catch (Exception ex)
|
| | | {
|
| | | Debug.LogError($"[Launch] UpdateBackgroundAsync exception: {ex}");
|
| | | TraceEnd("UpdateBackgroundAsync", "启动背景更新", "结果: 异常");
|
| | | }
|
| | | }
|
| | | #endif
|
| | |
| | | // 启动入口
|
| | | async void Start()
|
| | | {
|
| | | float startTime = Time.realtimeSinceStartup;
|
| | | TraceBegin("Start", "冷启动入口");
|
| | | Debug.LogError("[Launch] Start");
|
| | | #if UNITY_WEBGL
|
| | | await VersionConfigEx.Get();
|
| | |
| | | InitSetting();
|
| | |
|
| | | // 1. 打开加载界面
|
| | | LogTiming("Start", startTime);
|
| | | TraceEnd("Start", "冷启动入口", "结果: 已进入LocalRes流程");
|
| | | }
|
| | |
|
| | | /// <summary>
|
| | |
| | | /// </summary>
|
| | | public async UniTask InitYooAssetEarlyAsync()
|
| | | {
|
| | | float startTime = Time.realtimeSinceStartup;
|
| | | TraceBegin("InitYooAssetEarlyAsync", "资源系统预初始化");
|
| | | try
|
| | | {
|
| | | YooAsset.IRemoteServices remoteServices = null;
|
| | |
| | | if (YooAssetInitializer.Instance.State == YooAssetInitializer.InitState.InitFailed)
|
| | | {
|
| | | Debug.LogError($"[Launch] YooAsset early init failed: {YooAssetInitializer.Instance.LastError}");
|
| | | TraceEnd("InitYooAssetEarlyAsync", "资源系统预初始化", "结果: 初始化失败");
|
| | | return;
|
| | | }
|
| | |
|
| | |
| | | // HostPlayMode: 不预下载全部资源。YooAsset CacheFileSystem 在 LoadAssetAsync 时自动按需从 CDN 下载。
|
| | |
|
| | | Debug.LogError("[Launch] YooAsset early init completed. State: " + YooAssetInitializer.Instance.State);
|
| | | TraceEnd("InitYooAssetEarlyAsync", "资源系统预初始化", "结果: 初始化完成");
|
| | | }
|
| | | catch (Exception ex)
|
| | | {
|
| | | Debug.LogError($"[Launch] YooAsset early init exception: {ex}");
|
| | | }
|
| | | finally
|
| | | {
|
| | | LogTiming("InitYooAssetEarlyAsync", startTime);
|
| | | TraceEnd("InitYooAssetEarlyAsync", "资源系统预初始化", "结果: 异常");
|
| | | }
|
| | | }
|
| | |
|
| | | private void InitSetting()
|
| | | {
|
| | | float startTime = Time.realtimeSinceStartup;
|
| | | TraceBegin("InitSetting", "启动设置初始化");
|
| | | System.Globalization.CultureInfo culture = System.Globalization.CultureInfo.CreateSpecificCulture("en-US");
|
| | | System.Globalization.CultureInfo.CurrentCulture = culture;
|
| | | System.Globalization.CultureInfo.CurrentUICulture = culture;
|
| | |
| | |
|
| | | // 任何都要去请求版本信息
|
| | | LocalResManager.step = LocalResManager.LoadDllStep.RequestVersion;
|
| | | LogTiming("InitSetting", startTime);
|
| | | TraceEnd("InitSetting", "启动设置初始化");
|
| | | }
|
| | |
|
| | | private void InitPlugins()
|
| | | {
|
| | | float startTime = Time.realtimeSinceStartup;
|
| | | TraceBegin("InitPlugins", "插件初始化");
|
| | | DOTween.Init();
|
| | | LogTiming("InitPlugins", startTime);
|
| | | TraceEnd("InitPlugins", "插件初始化");
|
| | | }
|
| | |
|
| | | private void SDKInit()
|
| | | {
|
| | | float startTime = Time.realtimeSinceStartup;
|
| | | TraceBegin("SDKInit", "SDK初始化");
|
| | | AotSdkUtility.Instance.Init();
|
| | | LogTiming("SDKInit", startTime);
|
| | | TraceEnd("SDKInit", "SDK初始化");
|
| | | }
|
| | |
|
| | | /// <summary>
|
| | |
| | | if (launchExWin != null || isOpeningLaunchUI)
|
| | | return;
|
| | |
|
| | | float startTime = Time.realtimeSinceStartup;
|
| | | TraceBegin("ShowLaunchUIAsync", "启动UI动态层");
|
| | | isOpeningLaunchUI = true;
|
| | | try
|
| | | {
|
| | |
| | | finally
|
| | | {
|
| | | isOpeningLaunchUI = false;
|
| | | LogTiming("ShowLaunchUIAsync", startTime);
|
| | | TraceEnd("ShowLaunchUIAsync", "启动UI动态层");
|
| | | }
|
| | | }
|
| | |
|
| | |
| | | /// </summary>
|
| | | private void LoadMetadataForAOTAssemblies()
|
| | | {
|
| | | float startTime = Time.realtimeSinceStartup;
|
| | | TraceBegin("LoadMetadataForAOTAssemblies", "AOT元数据加载");
|
| | | /// 注意,补充元数据是给 AOT dll 补充元数据,而不是给热更新 dll 补充元数据。
|
| | | /// 热更新 dll 不缺元数据,不需要补充,如果调用 LoadMetadataForAOTAssembly 会返回错误。
|
| | | ///
|
| | |
| | | LoadImageErrorCode err = RuntimeApi.LoadMetadataForAOTAssembly(ReadBytesFromStreamingAssets(aotDllName), mode);
|
| | | Debug.LogError($"LoadMetadataForAOTAssembly:{aotDllName}. mode:{mode} ret:{err}");
|
| | | }
|
| | | LogTiming("LoadMetadataForAOTAssemblies", startTime);
|
| | | TraceEnd("LoadMetadataForAOTAssemblies", "AOT元数据加载");
|
| | | }
|
| | |
|
| | | public static byte[] ReadBytesFromStreamingAssets(string dllName)
|
| | |
| | |
|
| | | private void StartGame()
|
| | | {
|
| | | float startTime = Time.realtimeSinceStartup;
|
| | | TraceBegin("StartGame", "进入热更主流程");
|
| | | #if !UNITY_EDITOR
|
| | | LoadMetadataForAOTAssemblies();
|
| | | _hotUpdateAss = Assembly.Load(ReadBytesFromStreamingAssets("Main.dll.bytes"));
|
| | |
| | | }
|
| | | LocalResManager.Instance.RecordLauchEvent(3);
|
| | | Debug.LogError("进入游戏流程");
|
| | | LogTiming("StartGame", startTime);
|
| | | TraceEnd("StartGame", "进入热更主流程", "结果: LaunchInHot.Instance 已触发");
|
| | | }
|
| | |
|
| | | private void DestroySingleton()
|
| | |
| | | /// </summary>
|
| | | private async UniTask ReadDllBytes(Action callback)
|
| | | {
|
| | | float startTime = Time.realtimeSinceStartup;
|
| | | TraceBegin("ReadDllBytes", "热更DLL加载");
|
| | | var dllPackage = YooAssets.TryGetPackage("Dll");
|
| | | if (dllPackage == null)
|
| | | {
|
| | | Debug.LogError("[Launch] Dll package not found! 请确保已初始化 Dll 包。");
|
| | | DllLoadProgress = 1f;
|
| | | TraceEnd("ReadDllBytes", "热更DLL加载", "结果: Dll包缺失");
|
| | | callback?.Invoke();
|
| | | return;
|
| | | }
|
| | |
| | | DllLoadProgress = (float)(i + 1) / total;
|
| | | }
|
| | |
|
| | | LogTiming("ReadDllBytes", startTime);
|
| | | TraceEnd("ReadDllBytes", "热更DLL加载", $"DLL数量: {ALL_DLL_NAMES.Length}");
|
| | | callback?.Invoke();
|
| | | }
|
| | |
|