三国卡牌客户端基础资源仓库
Assets/Launch/Launch.cs
@@ -1,26 +1,39 @@
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
    {
@@ -29,6 +42,8 @@
            return m_Instance;
        }
    }
    private GameObject launchExWin = null;
    private static Launch m_Instance;
    private void Awake()
@@ -46,53 +61,299 @@
    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;
    }
}