华佗热更新模块

HotfixManager.cs 7.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237
  1. using TFramework;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using UnityEngine;
  5. using System;
  6. using UnityEngine.Networking;
  7. #if HybridCLR
  8. using HybridCLR;
  9. #endif
  10. namespace TModule.Runtime {
  11. public class HotfixManager : MonoBehaviour
  12. {
  13. public bool m_isEnableHotfix = true;
  14. public string m_resServerUrl;
  15. public string m_updateHandle;
  16. public string m_versionFileName;
  17. /// <summary>
  18. ///本地资源存放路径
  19. /// </summary>
  20. public string LocalResFilePath { get; private set; }
  21. /// <summary>
  22. /// 本地版本文件存放路径
  23. /// </summary>
  24. private string _loaclVersionPath;
  25. /// <summary>
  26. /// 待下载资源列表
  27. /// </summary>
  28. private List<VersionDataEntity> _NeedDownloadResList = new List<VersionDataEntity>();
  29. /// <summary>
  30. /// 本地资源列表
  31. /// </summary>
  32. private List<VersionDataEntity> _LocalResList = new List<VersionDataEntity>();
  33. /// <summary>
  34. /// 检查版本文件进行事件
  35. /// </summary>
  36. private Action<float> OnCheckVersion;
  37. /// <summary>
  38. /// 版本文件检查完成事件
  39. /// </summary>
  40. private Action<float> OnCheckVersionOver;
  41. /// <summary>
  42. /// 版本文件检查出错事件
  43. /// </summary>
  44. private Action<string> OnCheckVersionError;
  45. /// <summary>
  46. /// 检查进度
  47. /// </summary>
  48. private float _checkProgress;
  49. /// <summary>
  50. /// 超时时间
  51. /// </summary>
  52. public const int DOWNLOADTIMEOUT = 5;
  53. public float TotalSize
  54. {
  55. get
  56. {
  57. float szie = 0;
  58. _NeedDownloadResList.ForEach(p => szie += (float)p.m_size);
  59. return szie;
  60. }
  61. }
  62. void Start()
  63. {
  64. if (m_isEnableHotfix)
  65. {
  66. }
  67. }
  68. /// <summary>
  69. /// 检查版本文件
  70. /// </summary>
  71. public void InitCheckVersion(Action<float> checkVersion, Action<float> checkVersionOver, Action<string> checkVersionError = null)
  72. {
  73. if (string.IsNullOrEmpty(AssetConfig.ResUrl))
  74. {
  75. Log.Error("未指定资源服务器地址");
  76. return;
  77. }
  78. OnCheckVersion = checkVersion;
  79. OnCheckVersionOver = checkVersionOver;
  80. OnCheckVersionError = checkVersionError;
  81. _checkProgress = 0;
  82. string strVersionPath = AssetConfig.ResUrl + m_versionFileName;
  83. StartCoroutine(DowloadVersion(strVersionPath));
  84. }
  85. /// <summary>
  86. /// 对比版本文件
  87. /// </summary>
  88. /// <param name="arg0"></param>
  89. private void OnInitVersionCallBack(List<VersionDataEntity> arg0)
  90. {
  91. _loaclVersionPath = LocalResFilePath + m_versionFileName;
  92. if (FileOperations.FileExists(_loaclVersionPath))
  93. {
  94. List<VersionDataEntity> clienData = FileOperations.ReadJsonData<List<VersionDataEntity>>(_loaclVersionPath);
  95. foreach (var item in arg0)
  96. {
  97. _checkProgress += 1.0f / arg0.Count / 2;
  98. _checkProgress = _checkProgress >= 1 ? 1 : _checkProgress;
  99. OnCheckVersion?.Invoke(_checkProgress);
  100. VersionDataEntity dataEntity = clienData.Find(p => p.m_fullName == item.m_fullName);
  101. //已有资源
  102. if (dataEntity != null)
  103. {
  104. //对比MD5
  105. if (dataEntity.m_md5 != item.m_md5)
  106. _NeedDownloadResList.Add(item);
  107. }
  108. else
  109. _NeedDownloadResList.Add(item);//新资源
  110. }
  111. }
  112. else
  113. arg0.ForEach(p =>
  114. {
  115. _NeedDownloadResList.Add(p);
  116. _checkProgress += 1.0f / arg0.Count / 2;
  117. _checkProgress = _checkProgress >= 1 ? 1 : _checkProgress;
  118. OnCheckVersion?.Invoke(_checkProgress);
  119. });
  120. if (_NeedDownloadResList.Count > 0)
  121. OnCheckVersionOver?.Invoke(TotalSize);
  122. else
  123. OnCheckVersionOver?.Invoke(0);
  124. }
  125. /// <summary>
  126. /// 下载版本文件
  127. /// </summary>
  128. /// <param name="url"></param>
  129. /// <param name="OnDownLoadOver"></param>
  130. /// <returns></returns>
  131. private IEnumerator DowloadVersion(string url)
  132. {
  133. UnityWebRequest www = UnityWebRequest.Get(url);
  134. www.timeout = DOWNLOADTIMEOUT;
  135. yield return www.SendWebRequest();
  136. while (!www.isDone)
  137. {
  138. _checkProgress = www.downloadProgress / 2;
  139. OnCheckVersion?.Invoke(_checkProgress);
  140. }
  141. if (www.result != UnityWebRequest.Result.ProtocolError && www.result != UnityWebRequest.Result.ConnectionError)
  142. {
  143. string content = www.downloadHandler.text;
  144. _checkProgress = 0.5f;
  145. OnInitVersionCallBack(content.JsonStrToObject<List<VersionDataEntity>>());
  146. Debug.Log(content);
  147. }
  148. else
  149. {
  150. Debug.Log("下载失败:" + www.error);
  151. OnCheckVersionError?.Invoke(www.error);
  152. }
  153. }
  154. /// <summary>
  155. /// 保存版本文件
  156. /// </summary>
  157. private void SaveLoaclVersion() => FileOperations.WriteJsonData(_LocalResList, _loaclVersionPath);
  158. /// <summary>
  159. /// 更新版本文件
  160. /// </summary>
  161. /// <param name="entity"></param>
  162. public void ModifyLocaData(VersionDataEntity entity)
  163. {
  164. if (_LocalResList == null) return;
  165. bool isExists = false;
  166. for (int i = 0; i < _LocalResList.Count; i++)
  167. {
  168. if (_LocalResList[i].m_fullName.Equals(entity.m_fullName, StringComparison.CurrentCultureIgnoreCase))
  169. {
  170. _LocalResList[i].m_md5 = entity.m_md5;
  171. _LocalResList[i].m_fullName = entity.m_fullName;
  172. _LocalResList[i].m_size = entity.m_size;
  173. isExists = true;
  174. break;
  175. }
  176. }
  177. if (!isExists)
  178. {
  179. _LocalResList.Add(entity);
  180. }
  181. SaveLoaclVersion();
  182. }
  183. private static void LoadMetadataForAOTAssemblies()
  184. {
  185. List<string> aotMetaAssemblyFiles = new List<string>()
  186. {
  187. "mscorlib.dll",
  188. "System.dll",
  189. "System.Core.dll",
  190. };
  191. /// 注意,补充元数据是给AOT dll补充元数据,而不是给热更新dll补充元数据。
  192. /// 热更新dll不缺元数据,不需要补充,如果调用LoadMetadataForAOTAssembly会返回错误
  193. ///
  194. #if HybridCLR
  195. HomologousImageMode mode = HomologousImageMode.SuperSet;
  196. foreach (var aotDllName in aotMetaAssemblyFiles)
  197. {
  198. byte[] dllBytes = FileOperations.SafeReadAllBytes(AssetConfig.AssetBundleRootPath + aotDllName + ".bytes");
  199. // 加载assembly对应的dll,会自动为它hook。一旦aot泛型函数的native函数不存在,用解释器版本代码
  200. LoadImageErrorCode err = RuntimeApi.LoadMetadataForAOTAssembly(dllBytes, mode);
  201. Debug.Log($"LoadMetadataForAOTAssembly:{aotDllName}. mode:{mode} ret:{err}");
  202. }
  203. #else
  204. Log.Error("当前非热更环境!请先安装HybridCLR");
  205. #endif
  206. }
  207. }
  208. }