using HybridCLR;
|
using System;
|
using System.Collections;
|
using System.Collections.Generic;
|
using System.IO;
|
using System.Linq;
|
using System.Reflection;
|
using UnityEngine;
|
using UnityEngine.Networking;
|
using StartAotSDK;
|
//AOT集用自己专属命名空间的名字,避免与热更集冲突
|
//理论热更集和AOT集可以通用模块,如下载模块 LitJson等代码
|
//但是LitJson在AOT的情况下,如果热更集使用 如LitJson.JsonMapper.ToObject<另一个命名空间.Class>会报错无法解析, 该情况无法通过补充元数据解决
|
//同时官方保证热更集下是可以随意使用泛型,所以非必要情况下热更集不要用AOT集的模块, LitJson放在热更集各自使用即可,AOT用新的命名空间LitJsonForAot
|
namespace StartAot
|
{
|
public class LoadDll : MonoBehaviour
|
{
|
[SerializeField] Transform m_UICanvas;
|
void Start()
|
{
|
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 UNITY_EDITOR
|
//内网下载测试
|
_hotUpdateAss = System.AppDomain.CurrentDomain.GetAssemblies().First(a => a.GetName().Name == "Assembly-CSharp");
|
Type type = _hotUpdateAss.GetType("InGameDownTestUtility");
|
ResourcesModel.Instance.isPCTestDownLoad = (bool)type.GetMethod("GetReadVerionEx").Invoke(null, null);
|
#endif
|
AotSdkUtility.Instance.Init();
|
ResourcesModel.Instance.Init();
|
ResourcesModel.Instance.InitTable(() =>
|
{
|
ResourcesModel.Instance.InitDefaultLanguage();
|
ResourcesModel.Instance.OpenWindow("LaunchExWin", m_UICanvas);
|
#if !UNITY_EDITOR
|
ResourcesModel.step = ResourcesModel.LoadDllStep.RequestVersion;
|
#else
|
if (ResourcesModel.Instance.isPCTestDownLoad)
|
{
|
ResourcesModel.step = ResourcesModel.LoadDllStep.RequestVersion;
|
}
|
else
|
{
|
StartGame();
|
}
|
#endif
|
});
|
}
|
|
|
#region read bytes
|
|
private static Dictionary<string, byte[]> s_assetDatas = new Dictionary<string, byte[]>();
|
|
public static byte[] ReadBytesFromStreamingAssets(string dllName)
|
{
|
return s_assetDatas[dllName];
|
}
|
|
private string GetWebRequestPath(string asset)
|
{
|
var path = ResourcesModel.Instance.GetAssetFilePath(string.Concat(ResourcesModel.bytesFolderName, asset));
|
|
if (!path.Contains("file:"))
|
{
|
//ExternalStorePath 路径需要添加
|
path = "file://" + path;
|
}
|
return path;
|
}
|
|
//网络获取
|
private static List<string> AOTMetaAssemblyFiles { get; } = new List<string>();
|
|
IEnumerator ReadDllBytes(Action OnComplete)
|
{
|
foreach (var assetVersion in ResourcesModel.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);
|
yield return 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 == "Assembly-CSharp.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;
|
}
|
}
|
|
OnComplete();
|
}
|
|
#endregion
|
|
private static Assembly _hotUpdateAss;
|
|
/// <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 == "Assembly-CSharp.dll.bytes")
|
continue;
|
// 加载assembly对应的dll,会自动为它hook。一旦aot泛型函数的native函数不存在,用解释器版本代码
|
LoadImageErrorCode err = RuntimeApi.LoadMetadataForAOTAssembly(ReadBytesFromStreamingAssets(aotDllName), mode);
|
Debug.Log($"LoadMetadataForAOTAssembly:{aotDllName}. mode:{mode} ret:{err}");
|
}
|
}
|
|
void StartGame()
|
{
|
#if !UNITY_EDITOR
|
LoadMetadataForAOTAssemblies();
|
_hotUpdateAss = Assembly.Load(ReadBytesFromStreamingAssets("Assembly-CSharp.dll.bytes"));
|
s_assetDatas = null;
|
#else
|
if (_hotUpdateAss == null)
|
_hotUpdateAss = System.AppDomain.CurrentDomain.GetAssemblies().First(a => a.GetName().Name == "Assembly-CSharp");
|
#endif
|
ResourcesModel.step = ResourcesModel.LoadDllStep.None;
|
m_UICanvas.gameObject.SetActive(false);
|
DestroySingleton();
|
|
Type type = _hotUpdateAss.GetType("Launch");
|
GameObject go = new GameObject("Launch");
|
go.AddComponent(type);
|
Debug.Log("进入游戏流程");
|
}
|
|
private void DestroySingleton()
|
{
|
if (ResourcesModel.IsValid())
|
{
|
ResourcesModel.Instance.Destroy();
|
Destroy(ResourcesModel.Instance); //这里会延迟
|
}
|
if (DownloadMgr.IsValid())
|
{
|
Destroy(DownloadMgr.Instance);
|
}
|
if (LogicEngine.IsValid())
|
{
|
Destroy(LogicEngine.Instance);
|
}
|
if (HttpRequest.IsValid())
|
{
|
Destroy(HttpRequest.Instance);
|
}
|
if (DownLoadAndDiscompressTask.IsValid())
|
{
|
DownLoadAndDiscompressTask.Destroy();
|
}
|
stop = true;
|
}
|
|
bool stop = false;
|
|
void Update()
|
{
|
if (stop)
|
return;
|
if (ResourcesModel.step == ResourcesModel.LoadDllStep.None || ResourcesModel.step == ResourcesModel.LoadDllStep.Wait)
|
return;
|
else if (ResourcesModel.step == ResourcesModel.LoadDllStep.RequestVersion)
|
{
|
ResourcesModel.step = ResourcesModel.LoadDllStep.Wait;
|
ResourcesModel.Instance.RequestVersionCheck();
|
}
|
else if (ResourcesModel.step == ResourcesModel.LoadDllStep.PrepareDownLoad)
|
{
|
ResourcesModel.step = ResourcesModel.LoadDllStep.Wait;
|
//下载前准备,读表判断是否需要多语言不同下载路径
|
PrepareDownLoad();
|
}
|
else if (ResourcesModel.step == ResourcesModel.LoadDllStep.DownLoad)
|
{
|
ResourcesModel.step = ResourcesModel.LoadDllStep.Wait;
|
BeginDownload();
|
}
|
else if (ResourcesModel.step == ResourcesModel.LoadDllStep.ReadBytes)
|
{
|
ResourcesModel.step = ResourcesModel.LoadDllStep.Wait;
|
StartCoroutine(ReadDllBytes(this.StartGame));
|
}
|
//else if (ResourcesModel.step == ResourcesModel.LoadDllStep.Completed)
|
//{
|
// ResourcesModel.step = ResourcesModel.LoadDllStep.None;
|
// m_UICanvas.gameObject.SetActive(false);
|
// DestroySingleton();
|
//}
|
}
|
|
|
|
private void PrepareDownLoad()
|
{
|
|
ResourcesModel.Instance.RequestLogicBytes();
|
|
}
|
|
private void BeginDownload()
|
{
|
List<AssetVersion> priorDownLoadAssetVersions = new List<AssetVersion>();
|
foreach (var assetVersion in ResourcesModel.Instance.assetVersions.Values)
|
{
|
AssetVersion localAssetVersion = null;
|
ResourcesModel.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 = ResourcesModel.Instance.ExternalStorePath;
|
if (!Directory.Exists(targetDirectory))
|
{
|
Directory.CreateDirectory(targetDirectory);
|
}
|
|
DownLoadAndDiscompressTask.Instance.Prepare(priorDownLoadAssetVersions, DownloadComplete);
|
}
|
|
void DownloadComplete()
|
{
|
ResourcesModel.step = ResourcesModel.LoadDllStep.ReadBytes;
|
}
|
}
|
}
|