UpdateManifestOperation.cs 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358
  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using System.IO;
  5. namespace YooAsset
  6. {
  7. /// <summary>
  8. /// 更新清单操作
  9. /// </summary>
  10. public abstract class UpdateManifestOperation : AsyncOperationBase
  11. {
  12. /// <summary>
  13. /// 是否发现了新的补丁清单
  14. /// </summary>
  15. public bool FoundNewManifest { protected set; get; }
  16. }
  17. /// <summary>
  18. /// 编辑器下模拟运行的更新清单操作
  19. /// </summary>
  20. internal sealed class EditorPlayModeUpdateManifestOperation : UpdateManifestOperation
  21. {
  22. internal override void Start()
  23. {
  24. Status = EOperationStatus.Succeed;
  25. }
  26. internal override void Update()
  27. {
  28. }
  29. }
  30. /// <summary>
  31. /// 离线模式的更新清单操作
  32. /// </summary>
  33. internal sealed class OfflinePlayModeUpdateManifestOperation : UpdateManifestOperation
  34. {
  35. internal override void Start()
  36. {
  37. Status = EOperationStatus.Succeed;
  38. }
  39. internal override void Update()
  40. {
  41. }
  42. }
  43. /// <summary>
  44. /// 联机模式的更新清单操作
  45. /// </summary>
  46. internal sealed class HostPlayModeUpdateManifestOperation : UpdateManifestOperation
  47. {
  48. private enum ESteps
  49. {
  50. None,
  51. LoadWebManifestHash,
  52. CheckWebManifestHash,
  53. LoadWebManifest,
  54. CheckWebManifest,
  55. InitVerifyingCache,
  56. UpdateVerifyingCache,
  57. Done,
  58. }
  59. private static int RequestCount = 0;
  60. private readonly HostPlayModeImpl _impl;
  61. private readonly int _resourceVersion;
  62. private readonly int _timeout;
  63. private ESteps _steps = ESteps.None;
  64. private UnityWebDataRequester _downloader1;
  65. private UnityWebDataRequester _downloader2;
  66. private PatchCacheVerifier _patchCacheVerifier;
  67. private float _verifyTime;
  68. internal HostPlayModeUpdateManifestOperation(HostPlayModeImpl impl, int resourceVersion, int timeout)
  69. {
  70. _impl = impl;
  71. _resourceVersion = resourceVersion;
  72. _timeout = timeout;
  73. #if UNITY_WEBGL
  74. _patchCacheVerifier = new PatchCacheVerifierWithoutThread();
  75. #else
  76. _patchCacheVerifier = new PatchCacheVerifierWithThread();
  77. #endif
  78. }
  79. internal override void Start()
  80. {
  81. RequestCount++;
  82. _steps = ESteps.LoadWebManifestHash;
  83. }
  84. internal override void Update()
  85. {
  86. if (_steps == ESteps.None || _steps == ESteps.Done)
  87. return;
  88. if (_steps == ESteps.LoadWebManifestHash)
  89. {
  90. string webURL = GetPatchManifestRequestURL(YooAssetSettingsData.GetPatchManifestHashFileName(_resourceVersion));
  91. YooLogger.Log($"Beginning to request patch manifest hash : {webURL}");
  92. _downloader1 = new UnityWebDataRequester();
  93. _downloader1.SendRequest(webURL, _timeout);
  94. _steps = ESteps.CheckWebManifestHash;
  95. }
  96. if (_steps == ESteps.CheckWebManifestHash)
  97. {
  98. if (_downloader1.IsDone() == false)
  99. return;
  100. // Check error
  101. if (_downloader1.HasError())
  102. {
  103. _steps = ESteps.Done;
  104. Status = EOperationStatus.Failed;
  105. Error = _downloader1.GetError();
  106. }
  107. else
  108. {
  109. string webManifestHash = _downloader1.GetText();
  110. string cachedManifestHash = GetSandboxPatchManifestFileHash(_resourceVersion);
  111. // 如果补丁清单文件的哈希值相同
  112. if (cachedManifestHash == webManifestHash)
  113. {
  114. YooLogger.Log($"Patch manifest file hash is not change : {webManifestHash}");
  115. LoadSandboxPatchManifest(_resourceVersion);
  116. FoundNewManifest = false;
  117. _steps = ESteps.InitVerifyingCache;
  118. }
  119. else
  120. {
  121. YooLogger.Log($"Patch manifest hash is change : {webManifestHash} -> {cachedManifestHash}");
  122. FoundNewManifest = true;
  123. _steps = ESteps.LoadWebManifest;
  124. }
  125. }
  126. _downloader1.Dispose();
  127. }
  128. if (_steps == ESteps.LoadWebManifest)
  129. {
  130. string webURL = GetPatchManifestRequestURL(YooAssetSettingsData.GetPatchManifestFileName(_resourceVersion));
  131. YooLogger.Log($"Beginning to request patch manifest : {webURL}");
  132. _downloader2 = new UnityWebDataRequester();
  133. _downloader2.SendRequest(webURL, _timeout);
  134. _steps = ESteps.CheckWebManifest;
  135. }
  136. if (_steps == ESteps.CheckWebManifest)
  137. {
  138. if (_downloader2.IsDone() == false)
  139. return;
  140. // Check error
  141. if (_downloader2.HasError())
  142. {
  143. _steps = ESteps.Done;
  144. Status = EOperationStatus.Failed;
  145. Error = _downloader2.GetError();
  146. }
  147. else
  148. {
  149. // 解析补丁清单
  150. if (ParseAndSaveRemotePatchManifest(_resourceVersion, _downloader2.GetText()))
  151. {
  152. _steps = ESteps.InitVerifyingCache;
  153. }
  154. else
  155. {
  156. _steps = ESteps.Done;
  157. Status = EOperationStatus.Failed;
  158. Error = $"URL : {_downloader2.URL} Error : remote patch manifest content is invalid";
  159. }
  160. }
  161. _downloader2.Dispose();
  162. }
  163. if (_steps == ESteps.InitVerifyingCache)
  164. {
  165. _patchCacheVerifier.InitVerifier(_impl.AppPatchManifest, _impl.LocalPatchManifest, false);
  166. _verifyTime = UnityEngine.Time.realtimeSinceStartup;
  167. _steps = ESteps.UpdateVerifyingCache;
  168. }
  169. if (_steps == ESteps.UpdateVerifyingCache)
  170. {
  171. Progress = _patchCacheVerifier.GetVerifierProgress();
  172. if (_patchCacheVerifier.UpdateVerifier())
  173. {
  174. _steps = ESteps.Done;
  175. Status = EOperationStatus.Succeed;
  176. float costTime = UnityEngine.Time.realtimeSinceStartup - _verifyTime;
  177. YooLogger.Log($"Verify result : Success {_patchCacheVerifier.VerifySuccessCount}, Fail {_patchCacheVerifier.VerifyFailCount}, Elapsed time {costTime} seconds");
  178. }
  179. }
  180. }
  181. /// <summary>
  182. /// 获取补丁清单请求地址
  183. /// </summary>
  184. private string GetPatchManifestRequestURL(string fileName)
  185. {
  186. // 轮流返回请求地址
  187. if (RequestCount % 2 == 0)
  188. return _impl.GetPatchDownloadFallbackURL(fileName);
  189. else
  190. return _impl.GetPatchDownloadMainURL(fileName);
  191. }
  192. /// <summary>
  193. /// 解析并保存远端请求的补丁清单
  194. /// </summary>
  195. private bool ParseAndSaveRemotePatchManifest(int updateResourceVersion, string content)
  196. {
  197. try
  198. {
  199. var remotePatchManifest = PatchManifest.Deserialize(content);
  200. _impl.SetLocalPatchManifest(remotePatchManifest);
  201. YooLogger.Log("Save remote patch manifest file.");
  202. string savePath = PathHelper.MakePersistentLoadPath(YooAssetSettingsData.GetPatchManifestFileName(updateResourceVersion));
  203. PatchManifest.Serialize(savePath, remotePatchManifest);
  204. return true;
  205. }
  206. catch (Exception e)
  207. {
  208. YooLogger.Error(e.ToString());
  209. return false;
  210. }
  211. }
  212. /// <summary>
  213. /// 加载沙盒内的补丁清单
  214. /// 注意:在加载本地补丁清单之前,已经验证过文件的哈希值
  215. /// </summary>
  216. private void LoadSandboxPatchManifest(int updateResourceVersion)
  217. {
  218. YooLogger.Log("Load sandbox patch manifest file.");
  219. string filePath = PathHelper.MakePersistentLoadPath(YooAssetSettingsData.GetPatchManifestFileName(updateResourceVersion));
  220. string jsonData = File.ReadAllText(filePath);
  221. var sandboxPatchManifest = PatchManifest.Deserialize(jsonData);
  222. _impl.SetLocalPatchManifest(sandboxPatchManifest);
  223. }
  224. /// <summary>
  225. /// 获取沙盒内补丁清单文件的哈希值
  226. /// 注意:如果沙盒内补丁清单文件不存在,返回空字符串
  227. /// </summary>
  228. private string GetSandboxPatchManifestFileHash(int updateResourceVersion)
  229. {
  230. string filePath = PathHelper.MakePersistentLoadPath(YooAssetSettingsData.GetPatchManifestFileName(updateResourceVersion));
  231. if (File.Exists(filePath))
  232. return HashUtility.FileMD5(filePath);
  233. else
  234. return string.Empty;
  235. }
  236. }
  237. /// <summary>
  238. /// 联机模式的更新清单操作(弱联网)
  239. /// </summary>
  240. internal sealed class HostPlayModeWeaklyUpdateManifestOperation : UpdateManifestOperation
  241. {
  242. private enum ESteps
  243. {
  244. None,
  245. LoadSandboxManifestHash,
  246. InitVerifyingCache,
  247. UpdateVerifyingCache,
  248. Done,
  249. }
  250. private readonly HostPlayModeImpl _impl;
  251. private readonly int _resourceVersion;
  252. private ESteps _steps = ESteps.None;
  253. private PatchCacheVerifier _patchCacheVerifier;
  254. private float _verifyTime;
  255. internal HostPlayModeWeaklyUpdateManifestOperation(HostPlayModeImpl impl, int resourceVersion)
  256. {
  257. _impl = impl;
  258. _resourceVersion = resourceVersion;
  259. #if UNITY_WEBGL
  260. _patchCacheVerifier = new PatchCacheVerifierWithoutThread();
  261. #else
  262. _patchCacheVerifier = new PatchCacheVerifierWithThread();
  263. #endif
  264. }
  265. internal override void Start()
  266. {
  267. _steps = ESteps.LoadSandboxManifestHash;
  268. }
  269. internal override void Update()
  270. {
  271. if (_steps == ESteps.None || _steps == ESteps.Done)
  272. return;
  273. if (_steps == ESteps.LoadSandboxManifestHash)
  274. {
  275. LoadSandboxPatchManifest(_resourceVersion);
  276. _steps = ESteps.InitVerifyingCache;
  277. }
  278. if (_steps == ESteps.InitVerifyingCache)
  279. {
  280. if (_patchCacheVerifier.InitVerifier(_impl.AppPatchManifest, _impl.LocalPatchManifest, true))
  281. {
  282. _verifyTime = UnityEngine.Time.realtimeSinceStartup;
  283. _steps = ESteps.UpdateVerifyingCache;
  284. }
  285. else
  286. {
  287. _steps = ESteps.Done;
  288. Status = EOperationStatus.Failed;
  289. Error = $"The resource version {_resourceVersion} content is not complete !";
  290. }
  291. }
  292. if (_steps == ESteps.UpdateVerifyingCache)
  293. {
  294. Progress = _patchCacheVerifier.GetVerifierProgress();
  295. if (_patchCacheVerifier.UpdateVerifier())
  296. {
  297. float costTime = UnityEngine.Time.realtimeSinceStartup - _verifyTime;
  298. YooLogger.Log($"Verify result : Success {_patchCacheVerifier.VerifySuccessCount}, Fail {_patchCacheVerifier.VerifyFailCount}, Elapsed time {costTime} seconds");
  299. if (_patchCacheVerifier.VerifyFailCount > 0)
  300. {
  301. _steps = ESteps.Done;
  302. Status = EOperationStatus.Failed;
  303. Error = $"The resource version {_resourceVersion} content has verify failed file !";
  304. }
  305. else
  306. {
  307. _steps = ESteps.Done;
  308. Status = EOperationStatus.Succeed;
  309. }
  310. }
  311. }
  312. }
  313. /// <summary>
  314. /// 加载沙盒内的补丁清单
  315. /// 注意:在加载本地补丁清单之前,未验证过文件的哈希值
  316. /// </summary>
  317. private void LoadSandboxPatchManifest(int updateResourceVersion)
  318. {
  319. string filePath = PathHelper.MakePersistentLoadPath(YooAssetSettingsData.GetPatchManifestFileName(updateResourceVersion));
  320. if (File.Exists(filePath))
  321. {
  322. YooLogger.Log("Load sandbox patch manifest file.");
  323. string jsonData = File.ReadAllText(filePath);
  324. var sandboxPatchManifest = PatchManifest.Deserialize(jsonData);
  325. _impl.SetLocalPatchManifest(sandboxPatchManifest);
  326. }
  327. }
  328. }
  329. }