| | |
| | | using System.Collections;
|
| | | using System.Collections.Generic;
|
| | | using UnityEngine;
|
| | | using Cysharp.Threading;
|
| | | using Cysharp.Threading.Tasks;
|
| | | using System;
|
| | | using DG.Tweening;
|
| | | using System.Collections;
|
| | | using System.Collections.Generic;
|
| | | using System.IO;
|
| | | using System.Linq;
|
| | | using System.Reflection;
|
| | | using UnityEngine.Networking;
|
| | | using LaunchCommon;
|
| | | using HybridCLR;
|
| | |
|
| | | public class Launch : MonoBehaviour
|
| | | {
|
| | | public string versionUrl;
|
| | |
|
| | | // 是否开启热更
|
| | | public bool IsHotupdate = false;
|
| | |
|
| | | public bool IsUseSDK = false;
|
| | | #if UNITY_EDITOR
|
| | | public bool EnableNetLog = true;
|
| | | public bool EnableLog = true;
|
| | | public bool EnableLogWarning = true;
|
| | | public bool EnableLogError = true;
|
| | | #else
|
| | | public bool EnableNetLog = false;
|
| | | public bool EnableLog = false;
|
| | | public bool EnableLogWarning = false;
|
| | | public bool EnableLogError = false;
|
| | | #endif
|
| | |
|
| | | public bool versionCheck = false;
|
| | | private static Assembly _hotUpdateAss;
|
| | |
|
| | | public bool IsUseAssetBundle = false;
|
| | | //网络获取
|
| | | private static List<string> AOTMetaAssemblyFiles { get; } = new List<string>();
|
| | |
|
| | | // 状态机
|
| | | private LaunchStateMachine stateMachine;
|
| | | private static Dictionary<string, byte[]> s_assetDatas = new Dictionary<string, byte[]>();
|
| | |
|
| | | public static Launch Instance
|
| | | {
|
| | |
| | | return m_Instance;
|
| | | }
|
| | | }
|
| | |
|
| | | private GameObject launchExWin = null;
|
| | |
|
| | | private static Launch m_Instance;
|
| | | private void Awake()
|
| | |
| | | async void Start()
|
| | | {
|
| | | Debug.Log("Launch Start");
|
| | | |
| | |
|
| | | DOTween.Init();
|
| | | InitPlugins();
|
| | | InitSetting();
|
| | |
|
| | | // 1. 打开加载界面
|
| | | LaunchLoadingWin.OpenWindow();
|
| | | |
| | | // 2. 初始化状态机
|
| | | InitStateMachine();
|
| | | |
| | | // 3. 启动状态机
|
| | | await stateMachine.Start();
|
| | | }
|
| | |
|
| | | // 初始化状态机
|
| | | private void InitStateMachine()
|
| | | private void InitSetting()
|
| | | {
|
| | | stateMachine = new LaunchStateMachine();
|
| | | System.Globalization.CultureInfo culture = System.Globalization.CultureInfo.CreateSpecificCulture("en-US");
|
| | | System.Globalization.CultureInfo.CurrentCulture = culture;
|
| | | System.Globalization.CultureInfo.CurrentUICulture = culture;
|
| | | System.Globalization.CultureInfo.DefaultThreadCurrentCulture = culture;
|
| | | System.Globalization.CultureInfo.DefaultThreadCurrentUICulture = culture;
|
| | | System.Net.ServicePointManager.DefaultConnectionLimit = 100;
|
| | |
|
| | | if (versionCheck)
|
| | | #if UNITY_EDITOR
|
| | | //内网下载测试
|
| | | _hotUpdateAss = System.AppDomain.CurrentDomain.GetAssemblies().First(a => a.GetName().Name == "Main");
|
| | | Type type = _hotUpdateAss.GetType("InGameDownTestUtility");
|
| | | LocalResManager.Instance.isPCTestDownLoad = (bool)type.GetMethod("GetReadVerionEx").Invoke(null, null);
|
| | | #endif
|
| | | SDKInit();
|
| | | LocalResManager.Instance.Init();
|
| | | LocalResManager.Instance.InitTable(() =>
|
| | | {
|
| | | // 添加状态
|
| | | // 1. 初始化SDK
|
| | | stateMachine.AddState(new InitSDKState());
|
| | | |
| | | // 2. 资源解压
|
| | | stateMachine.AddState(new ExtractResourcesState());
|
| | | |
| | | // 3. 版本检查
|
| | | CheckVersionState checkVersionState = new CheckVersionState(versionUrl);
|
| | | stateMachine.AddState(checkVersionState);
|
| | | |
| | | // 4. 热更新
|
| | | stateMachine.AddState(new HotUpdateState(IsHotupdate, LaunchUtility.UpdateType.None));
|
| | | LocalResManager.Instance.InitDefaultLanguage();
|
| | | launchExWin = LaunchExWin.OpenWindow();
|
| | | // LocalResManager.Instance.OpenWindow("LaunchExWin", m_UICanvas);
|
| | | #if !UNITY_EDITOR
|
| | | LocalResManager.step = LocalResManager.LoadDllStep.RequestVersion;
|
| | | #else
|
| | | if (LocalResManager.Instance.isPCTestDownLoad)
|
| | | {
|
| | | LocalResManager.step = LocalResManager.LoadDllStep.RequestVersion;
|
| | | }
|
| | | else
|
| | | {
|
| | | StartGame();
|
| | | }
|
| | | #endif
|
| | | });
|
| | | }
|
| | |
|
| | | private void InitPlugins()
|
| | | {
|
| | | DOTween.Init();
|
| | | }
|
| | |
|
| | | private void SDKInit()
|
| | | {
|
| | |
|
| | | }
|
| | | |
| | | /// <summary>
|
| | | /// 为aot assembly加载原始metadata, 这个代码放aot或者热更新都行。
|
| | | /// 一旦加载后,如果AOT泛型函数对应native实现不存在,则自动替换为解释模式执行
|
| | | /// </summary>
|
| | | private void LoadMetadataForAOTAssemblies()
|
| | | {
|
| | | /// 注意,补充元数据是给AOT dll补充元数据,而不是给热更新dll补充元数据。
|
| | | /// 热更新dll不缺元数据,不需要补充,如果调用LoadMetadataForAOTAssembly会返回错误
|
| | | /// |
| | | HomologousImageMode mode = HomologousImageMode.SuperSet;
|
| | | foreach (var aotDllName in AOTMetaAssemblyFiles)
|
| | | {
|
| | | if (aotDllName == "Main.dll.bytes")
|
| | | continue;
|
| | | // 加载assembly对应的dll,会自动为它hook。一旦aot泛型函数的native函数不存在,用解释器版本代码
|
| | | LoadImageErrorCode err = RuntimeApi.LoadMetadataForAOTAssembly(ReadBytesFromStreamingAssets(aotDllName), mode);
|
| | | Debug.Log($"LoadMetadataForAOTAssembly:{aotDllName}. mode:{mode} ret:{err}");
|
| | | }
|
| | | }
|
| | |
|
| | | public static byte[] ReadBytesFromStreamingAssets(string dllName)
|
| | | {
|
| | | return s_assetDatas[dllName];
|
| | | }
|
| | |
|
| | | private void StartGame()
|
| | | {
|
| | | #if !UNITY_EDITOR
|
| | | LoadMetadataForAOTAssemblies();
|
| | | _hotUpdateAss = Assembly.Load(ReadBytesFromStreamingAssets("Main.dll.bytes"));
|
| | | s_assetDatas = null;
|
| | | #else
|
| | | if (_hotUpdateAss == null)
|
| | | _hotUpdateAss = System.AppDomain.CurrentDomain.GetAssemblies().First(a => a.GetName().Name == "Main");
|
| | | #endif
|
| | | LocalResManager.step = LocalResManager.LoadDllStep.None;
|
| | | // m_UICanvas.gameObject.SetActive(false);
|
| | | DestroySingleton();
|
| | | Type type = _hotUpdateAss.GetType("LaunchInHot").BaseType;
|
| | |
|
| | | var getInstance = type.GetMethod("get_Instance", BindingFlags.Public | BindingFlags.Static);
|
| | | if (getInstance != null)
|
| | | {
|
| | | getInstance.Invoke(null, null);
|
| | | }
|
| | | else
|
| | | {
|
| | | Debug.LogError("无法找到get_Instance方法");
|
| | | }
|
| | | |
| | | Debug.Log("进入游戏流程");
|
| | | }
|
| | |
|
| | | private void DestroySingleton()
|
| | | {
|
| | | if (LocalResManager.IsValid())
|
| | | {
|
| | | LocalResManager.Destroy();
|
| | | }
|
| | | if (DownloadMgr.IsValid())
|
| | | {
|
| | | Destroy(DownloadMgr.Instance);
|
| | | }
|
| | | if (LogicEngine.IsValid())
|
| | | {
|
| | | Destroy(LogicEngine.Instance);
|
| | | }
|
| | | if (HttpRequest.IsValid())
|
| | | {
|
| | | Destroy(HttpRequest.Instance);
|
| | | }
|
| | | if (DownLoadAndDiscompressTask.IsValid())
|
| | | {
|
| | | DownLoadAndDiscompressTask.Destroy();
|
| | | }
|
| | |
|
| | | |
| | | // 5. 进入热更新程序集
|
| | | stateMachine.AddState(new EnterHotUpdateAssemblyState());
|
| | | |
| | | // 注册状态机完成事件
|
| | | stateMachine.OnCompleted += () => {
|
| | | Debug.Log("启动流程完成");
|
| | | };
|
| | | }
|
| | | |
| | | |
| | | // if (null != launchExWin)
|
| | | // {
|
| | | // Destroy(launchExWin);
|
| | | // launchExWin = null;
|
| | | // }
|
| | |
|
| | | stop = true;
|
| | | }
|
| | |
|
| | | bool stop = false;
|
| | |
|
| | | void Update()
|
| | | {
|
| | | if (stop)
|
| | | return;
|
| | | if (LocalResManager.step == LocalResManager.LoadDllStep.None || LocalResManager.step == LocalResManager.LoadDllStep.Wait)
|
| | | return;
|
| | | else if (LocalResManager.step == LocalResManager.LoadDllStep.RequestVersion)
|
| | | {
|
| | | LocalResManager.step = LocalResManager.LoadDllStep.Wait;
|
| | | LocalResManager.Instance.RequestVersionCheck();
|
| | | }
|
| | | else if (LocalResManager.step == LocalResManager.LoadDllStep.PrepareDownLoad)
|
| | | {
|
| | | LocalResManager.step = LocalResManager.LoadDllStep.Wait;
|
| | | //下载前准备,读表判断是否需要多语言不同下载路径
|
| | | PrepareDownLoad();
|
| | | }
|
| | | else if (LocalResManager.step == LocalResManager.LoadDllStep.DownLoad)
|
| | | {
|
| | | LocalResManager.step = LocalResManager.LoadDllStep.Wait;
|
| | | BeginDownload();
|
| | | }
|
| | | else if (LocalResManager.step == LocalResManager.LoadDllStep.ReadBytes)
|
| | | {
|
| | | LocalResManager.step = LocalResManager.LoadDllStep.Wait;
|
| | | ReadDllBytes(this.StartGame);
|
| | | }
|
| | | //else if (LocalResManager.step == LocalResManager.LoadDllStep.Completed)
|
| | | //{
|
| | | // LocalResManager.step = LocalResManager.LoadDllStep.None;
|
| | | // m_UICanvas.gameObject.SetActive(false);
|
| | | // DestroySingleton();
|
| | | //}
|
| | | }
|
| | |
|
| | |
|
| | | private string GetWebRequestPath(string asset)
|
| | | {
|
| | | var path = LocalResManager.Instance.GetAssetFilePath(string.Concat(LocalResManager.bytesFolderName, asset));
|
| | |
|
| | | if (!path.Contains("file:"))
|
| | | {
|
| | | //ExternalStorePath 路径需要添加
|
| | | path = "file://" + path;
|
| | | }
|
| | | return path;
|
| | | }
|
| | |
|
| | | private async void ReadDllBytes(Action callback)
|
| | | {
|
| | | foreach (var assetVersion in LocalResManager.Instance.assetVersions.Values)
|
| | | {
|
| | | if (assetVersion.localValid)
|
| | | {
|
| | | AOTMetaAssemblyFiles.Add(string.Concat(assetVersion.fileName, assetVersion.extersion));
|
| | | }
|
| | | else
|
| | | {
|
| | | Debug.LogErrorFormat("文件无效 {0}", assetVersion.fileName);
|
| | | }
|
| | | }
|
| | |
|
| | | foreach (var asset in AOTMetaAssemblyFiles)
|
| | | {
|
| | | string dllPath = GetWebRequestPath(asset);
|
| | | Debug.Log($"dllPath:{dllPath}");
|
| | | UnityWebRequest www = UnityWebRequest.Get(dllPath);
|
| | | await www.SendWebRequest();
|
| | |
|
| | | #if UNITY_2020_1_OR_NEWER
|
| | | if (www.result != UnityWebRequest.Result.Success)
|
| | | {
|
| | | Debug.Log(www.error);
|
| | | }
|
| | | #else
|
| | | if (www.isHttpError || www.isNetworkError)
|
| | | {
|
| | | Debug.Log(www.error);
|
| | | }
|
| | | #endif
|
| | | else
|
| | | {
|
| | | // 特殊处理
|
| | | byte[] assetData;
|
| | | if (asset == "Main.dll.bytes")
|
| | | {
|
| | | assetData = new byte[www.downloadHandler.data.Length - 3];
|
| | | Array.Copy(www.downloadHandler.data, 3, assetData, 0, assetData.Length);
|
| | | }
|
| | | else
|
| | | {
|
| | | assetData = www.downloadHandler.data;
|
| | | }
|
| | |
|
| | | Debug.Log($"dll:{asset} size:{assetData.Length}");
|
| | | s_assetDatas[asset] = assetData;
|
| | | }
|
| | | }
|
| | |
|
| | | callback?.Invoke();
|
| | | }
|
| | |
|
| | | private void PrepareDownLoad()
|
| | | {
|
| | | if (LocalResManager.downLoadCount > 3)
|
| | | {
|
| | | LocalResManager.step = LocalResManager.LoadDllStep.None;
|
| | | stop = true;
|
| | | HttpBehaviour.ConnectAllTimes = 9999;
|
| | | return;
|
| | | }
|
| | |
|
| | | LocalResManager.Instance.RequestLogicBytes();
|
| | | }
|
| | |
|
| | | private void BeginDownload()
|
| | | {
|
| | | List<AssetVersion> priorDownLoadAssetVersions = new List<AssetVersion>();
|
| | | foreach (var assetVersion in LocalResManager.Instance.assetVersions.Values)
|
| | | {
|
| | | AssetVersion localAssetVersion = null;
|
| | | LocalResManager.Instance.localAssetVersions.TryGetValue(assetVersion.relativePath, out localAssetVersion);
|
| | | if (!assetVersion.CheckLocalFileValid(localAssetVersion))
|
| | | {
|
| | | priorDownLoadAssetVersions.Add(assetVersion);
|
| | | assetVersion.localValid = false;
|
| | | }
|
| | | else
|
| | | {
|
| | | assetVersion.localValid = true;
|
| | | }
|
| | | }
|
| | |
|
| | | Debug.LogFormat("需要下载的文件数量:{0}", priorDownLoadAssetVersions.Count);
|
| | | if (priorDownLoadAssetVersions.Count == 0)
|
| | | {
|
| | | DownloadComplete();
|
| | | return;
|
| | | }
|
| | | var targetDirectory = LocalResManager.Instance.ExternalStorePath;
|
| | | if (!Directory.Exists(targetDirectory))
|
| | | {
|
| | | Directory.CreateDirectory(targetDirectory);
|
| | | }
|
| | |
|
| | | DownLoadAndDiscompressTask.Instance.Prepare(priorDownLoadAssetVersions, DownloadComplete);
|
| | | }
|
| | |
|
| | | void DownloadComplete()
|
| | | {
|
| | | LocalResManager.step = LocalResManager.LoadDllStep.ReadBytes;
|
| | | }
|
| | | }
|
| | |
|