UpdateHelper.cs 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688
  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. using System;
  5. using System.IO;
  6. using UnityEngine.Networking;
  7. /// <summary>
  8. /// 更新助手
  9. /// </summary>
  10. public class UpdateHelper : MonoBehaviour
  11. {
  12. /// <summary>
  13. /// 安卓更新文件名称
  14. /// </summary>
  15. public static string AndroidUpdateFileName = "UpdateFileAndroid.json";
  16. /// <summary>
  17. /// IOS更新文件名称
  18. /// </summary>
  19. public static string IosUpdateFileName = "UpdateFileIOS.json";
  20. /// <summary>
  21. /// 资源储存的根目录
  22. /// </summary>
  23. private string resSaveRootPath = "";
  24. /// <summary>
  25. /// 单键
  26. /// </summary>
  27. private static UpdateHelper mInst = null;
  28. /// <summary>
  29. /// 客户端的更新文件信息
  30. /// </summary>
  31. private UpdateFileInfo mClientUpdateFileInfo = null;
  32. /// <summary>
  33. /// 服务器上的更新文件信息
  34. /// </summary>
  35. private UpdateFileInfo mServerUpdateFileInfo = null;
  36. /// <summary>
  37. /// 服务器更新文件的数据
  38. /// </summary>
  39. private byte[] mServerUpdateFileDatas = null;
  40. /// <summary>
  41. /// 更新列表
  42. /// </summary>
  43. private List<string> mUpdateList = new List<string>();
  44. /// <summary>
  45. /// 开始更新的时间
  46. /// </summary>
  47. private float mUpdateTime = -1;
  48. /// <summary>
  49. /// 更新信息
  50. /// </summary>
  51. private UpdateInfo mUpdateInfo = new UpdateInfo();
  52. /// <summary>
  53. /// 更新迭代器
  54. /// </summary>
  55. private IEnumerator mUpdateIEnumerator = null;
  56. /// <summary>
  57. /// 超时时间
  58. /// </summary>
  59. private const float mTimeOut = 10.0f;
  60. /// <summary>
  61. /// 服务器地址
  62. /// </summary>
  63. private string mServerUrl = "";
  64. /// <summary>
  65. /// 获取单键实例
  66. /// </summary>
  67. /// <returns>单键实例</returns>
  68. public static UpdateHelper Instance()
  69. {
  70. return mInst;
  71. }
  72. /// <summary>
  73. /// 唤醒
  74. /// </summary>
  75. private void Awake()
  76. {
  77. mInst = this;
  78. resSaveRootPath = Application.persistentDataPath + "/Res/";
  79. }
  80. /// <summary>
  81. /// 获取更新文件列表
  82. /// </summary>
  83. public void GetUpdateFile(string serverUrl, Action ac)
  84. {
  85. Debug.Log("开始获取更新文件");
  86. mServerUrl = serverUrl;
  87. mUpdateTime = Time.time;
  88. ResetUpdateInfo();
  89. // 资源保存的路径
  90. if (!Directory.Exists(resSaveRootPath))
  91. {
  92. Directory.CreateDirectory(resSaveRootPath);
  93. }
  94. // 加载客户端更新文件
  95. LoadClientMD5FileInfo((md5) => { });
  96. // 加载服务器更新文件
  97. LoadServerMD5FileInfo(serverUrl, (md5) =>
  98. {
  99. if (ac != null)
  100. {
  101. ac();
  102. }
  103. });
  104. }
  105. /// <summary>
  106. /// 当前版本号
  107. /// </summary>
  108. /// <returns></returns>
  109. public void GetCurVersion(Action<int> ac)
  110. {
  111. LoadClientMD5FileInfo((info) =>
  112. {
  113. if (info != null)
  114. {
  115. // 基带版本>=本地版本清空沙盒
  116. if (GlobalConfig.ClientBsaeVersion >= mClientUpdateFileInfo.Version)
  117. {
  118. mUpdateInfo.mCurFileIndex = 1;
  119. mUpdateInfo.mCurFileProgress = 1;
  120. mUpdateInfo.mCurFlieName = "";
  121. mUpdateInfo.mTotalFileCount = 1;
  122. mUpdateInfo.mUpdateComplete = true;
  123. // 删除Res文件夹
  124. if (Directory.Exists(resSaveRootPath))
  125. {
  126. Debug.Log("版本已是最新,发现过期的资源文件,开始清理...");
  127. Directory.Delete(resSaveRootPath, true); // 直接删除路径
  128. Debug.Log("清理过期的资源文件成功...");
  129. return;
  130. }
  131. }
  132. ac(info.Version);
  133. }
  134. else
  135. {
  136. ac(1);
  137. }
  138. });
  139. }
  140. /// <summary>
  141. /// 获取完整版本号
  142. /// </summary>
  143. /// <returns></returns>
  144. public string GetFullVersion()
  145. {
  146. if (mServerUpdateFileInfo != null)
  147. {
  148. int midVer = mServerUpdateFileInfo.Version / 1000;
  149. int minVer = mServerUpdateFileInfo.Version % 1000;
  150. string fullVersion = GlobalConfig.ClientVersion + "." + midVer + "." + minVer;
  151. PlayerPrefs.SetString("Version", fullVersion);
  152. PlayerPrefs.Save();
  153. return fullVersion;
  154. }
  155. else if (mClientUpdateFileInfo != null)
  156. {
  157. int midVer = mClientUpdateFileInfo.Version / 1000;
  158. int minVer = mClientUpdateFileInfo.Version % 1000;
  159. string fullVersion = GlobalConfig.ClientVersion + "." + midVer + "." + minVer;
  160. PlayerPrefs.SetString("Version", fullVersion);
  161. PlayerPrefs.Save();
  162. return fullVersion;
  163. }
  164. else
  165. {
  166. return GlobalConfig.ClientVersion + ".0.0";
  167. }
  168. }
  169. /// <summary>
  170. /// 获取本地MD5文件
  171. /// </summary>
  172. /// <param name="ac"></param>
  173. public void LoadClientMD5FileInfo(Action<UpdateFileInfo> ac)
  174. {
  175. string fullUrl = "";
  176. // 加载客户端更新文件
  177. if (mClientUpdateFileInfo == null)
  178. {
  179. if (Application.platform == RuntimePlatform.IPhonePlayer)
  180. {
  181. fullUrl = GetPath(IosUpdateFileName);
  182. }
  183. else
  184. {
  185. fullUrl = GetPath(AndroidUpdateFileName);
  186. }
  187. StartCoroutine(IE_GetUpdateFile(fullUrl, false, (clientFile) =>
  188. {
  189. if (clientFile != null)
  190. {
  191. mClientUpdateFileInfo = clientFile;
  192. ac(mClientUpdateFileInfo);
  193. }
  194. else
  195. {
  196. ac(null);
  197. mUpdateTime = -1;
  198. mClientUpdateFileInfo = null;
  199. UI_CueDialog.Instance().Open("加载本地MD5文件失败!请稍后重试!", "更新提示", E_DialogType.OneButton, ExitGame);
  200. }
  201. }));
  202. }
  203. else
  204. {
  205. ac(mClientUpdateFileInfo);
  206. }
  207. }
  208. /// <summary>
  209. /// 加载服务端MD5文件
  210. /// </summary>
  211. /// <param name="serverUrl"></param>
  212. /// <param name="ac"></param>
  213. public void LoadServerMD5FileInfo(string serverUrl, Action<UpdateFileInfo> ac)
  214. {
  215. string fullUrl = "";
  216. if (mServerUpdateFileInfo == null)
  217. {
  218. if (Application.platform == RuntimePlatform.IPhonePlayer)
  219. {
  220. fullUrl = serverUrl + "/" + IosUpdateFileName;
  221. }
  222. else
  223. {
  224. fullUrl = serverUrl + "/" + AndroidUpdateFileName;
  225. }
  226. StartCoroutine(IE_GetUpdateFile(fullUrl, true, (serverFile) =>
  227. {
  228. if (serverFile != null)
  229. {
  230. mServerUpdateFileInfo = serverFile;
  231. ac(mServerUpdateFileInfo);
  232. }
  233. else
  234. {
  235. ac(null);
  236. mUpdateTime = -1;
  237. mServerUpdateFileInfo = null;
  238. UI_CueDialog.Instance().Open("从服务器获取更新列表失败!请稍后重试!", "更新提示", E_DialogType.OneButton, ExitGame);
  239. }
  240. }));
  241. }
  242. else
  243. {
  244. ac(mServerUpdateFileInfo);
  245. }
  246. }
  247. /// <summary>
  248. /// 开始更新
  249. /// </summary>
  250. public void CheckUpdate()
  251. {
  252. //当用户使用移动网络时,提醒用户是否继续更新
  253. if (Application.internetReachability == NetworkReachability.ReachableViaCarrierDataNetwork)
  254. {
  255. UI_CueDialog.Instance().Open("检测到您当前使用的是流量,更新将消耗您的流量,是否继续更新!", "更新提示", E_DialogType.TwoBntton, StartUpdate, ExitGame);
  256. }
  257. else
  258. {
  259. StartUpdate();
  260. }
  261. }
  262. /// <summary>
  263. /// 开始更新
  264. /// </summary>
  265. private void StartUpdate()
  266. {
  267. // 提取需要更新的列表
  268. mUpdateList.Clear();
  269. for (int i = 0; i < mServerUpdateFileInfo.Infos.Count; i++)
  270. {
  271. bool add = true;
  272. for (int j = 0; j < mClientUpdateFileInfo.Infos.Count; j++)
  273. {
  274. if (mClientUpdateFileInfo.Infos[j].Name == mServerUpdateFileInfo.Infos[i].Name)
  275. {
  276. if (mClientUpdateFileInfo.Infos[j].MD5 == mServerUpdateFileInfo.Infos[i].MD5)
  277. {
  278. // md5相同,无需重新下载
  279. add = false;
  280. break;
  281. }
  282. }
  283. }
  284. // 添加到更新列表
  285. if (add)
  286. {
  287. mUpdateList.Add(mServerUpdateFileInfo.Infos[i].Name);
  288. }
  289. }
  290. // 获取更新的断点续传信息
  291. string LastUpdateInfo = PlayerPrefs.GetString("YLSJ_LastUpdateInfo", "0_0");
  292. string[] strs = LastUpdateInfo.Split('_');
  293. int startIndex = 0;
  294. if (strs.Length == 2)
  295. {
  296. int version = int.Parse(strs[0]);
  297. int index = int.Parse(strs[1]);
  298. // 储存版本和服务版本一致,更新从记录的索引开始
  299. if (version == mServerUpdateFileInfo.Version)
  300. {
  301. startIndex = index;
  302. }
  303. }
  304. // 开始更新
  305. mUpdateIEnumerator = IE_DownLoadUpdateFile(startIndex);
  306. StartCoroutine(mUpdateIEnumerator);
  307. }
  308. /// <summary>
  309. /// 重设更新信息
  310. /// </summary>
  311. private void ResetUpdateInfo()
  312. {
  313. mClientUpdateFileInfo = null;
  314. mServerUpdateFileInfo = null;
  315. mUpdateInfo.mCurFileIndex = 0;
  316. mUpdateInfo.mCurFileProgress = 0;
  317. mUpdateInfo.mCurFlieName = "";
  318. mUpdateInfo.mTotalFileCount = 0;
  319. mUpdateInfo.mUpdateComplete = false;
  320. }
  321. /// <summary>
  322. /// 获取更新信息
  323. /// </summary>
  324. /// <returns></returns>
  325. public UpdateInfo GetUpdateInfo()
  326. {
  327. return mUpdateInfo;
  328. }
  329. /// <summary>
  330. /// 协同加载 更新文件
  331. /// </summary>
  332. /// <param name="call"> 当前的更新信息</param>
  333. /// <returns>返回资源AssetBundle</returns>
  334. private IEnumerator IE_DownLoadUpdateFile(int index)
  335. {
  336. if (index >= 0 && index < mUpdateList.Count)
  337. {
  338. mUpdateInfo.mTotalFileCount = mUpdateList.Count;
  339. string url = "";
  340. for (int i = index; i < mUpdateList.Count; i++)
  341. {
  342. // 加载服务器更新文件
  343. if (Application.platform == RuntimePlatform.IPhonePlayer)
  344. {
  345. url = mServerUrl + "UpdateIOS/" + mUpdateList[i];
  346. }
  347. else
  348. {
  349. url = mServerUrl + "UpdateAndroid/" + mUpdateList[i];
  350. }
  351. mUpdateInfo.mCurFileIndex = i;
  352. mUpdateInfo.mCurFlieName = mUpdateList[i];
  353. // 是否有错误
  354. bool isError = false;
  355. // 上次的进度
  356. float lastProgress = 0;
  357. // 上次进度的时间
  358. float lastProgresstime = Time.time;
  359. WWW www = new WWW(url);
  360. // 未完成,更新进度
  361. while (www.isDone == false)
  362. {
  363. if (lastProgress == www.progress)
  364. {
  365. // 检测是否超时
  366. if (lastProgresstime + mTimeOut <= Time.time)
  367. {
  368. isError = true;
  369. break;
  370. }
  371. }
  372. else
  373. {
  374. mUpdateInfo.mCurFileProgress = www.progress + 0.05f;
  375. lastProgress = www.progress;
  376. lastProgresstime = Time.time;
  377. }
  378. yield return null;
  379. }
  380. // 未超时,但有网络返回的错误
  381. if (isError == false && string.IsNullOrEmpty(www.error) == false)
  382. {
  383. continue;
  384. }
  385. // 成功
  386. if (isError == false)
  387. {
  388. mUpdateInfo.mCurFileProgress = 1;
  389. byte[] datas = www.bytes;
  390. // 保存
  391. string savePath = resSaveRootPath + mUpdateInfo.mCurFlieName;
  392. File.WriteAllBytes(savePath, datas);
  393. // 储存更新信息
  394. PlayerPrefs.SetString("YLSJ_LastUpdateInfo", mServerUpdateFileInfo.Version + "_" + (i + 1));
  395. PlayerPrefs.Save();
  396. }
  397. // 失败
  398. else
  399. {
  400. mServerUpdateFileDatas = null;
  401. UI_CueDialog.Instance().Open("更新失败!是否重试!", "更新提示", E_DialogType.TwoBntton, ReUpdate, ExitGame);
  402. break;
  403. }
  404. www.Dispose();
  405. yield return new WaitForSeconds(0.1f);
  406. }
  407. // 更新完成
  408. mUpdateInfo.mUpdateComplete = true;
  409. // 储存更新文件
  410. string fileSavePath = "";
  411. if (Application.platform == RuntimePlatform.IPhonePlayer)
  412. {
  413. fileSavePath = resSaveRootPath + IosUpdateFileName;
  414. }
  415. else
  416. {
  417. fileSavePath = resSaveRootPath + AndroidUpdateFileName;
  418. }
  419. if (mServerUpdateFileDatas != null)
  420. {
  421. File.WriteAllBytes(fileSavePath, mServerUpdateFileDatas);
  422. }
  423. }
  424. }
  425. /// <summary>
  426. /// 重新更新
  427. /// </summary>
  428. private void ReUpdate()
  429. {
  430. // 停止正在更新的
  431. if (mUpdateIEnumerator != null)
  432. {
  433. StopCoroutine(mUpdateIEnumerator);
  434. mUpdateIEnumerator = null;
  435. }
  436. // 开始更新
  437. mUpdateIEnumerator = IE_DownLoadUpdateFile(mUpdateInfo.mCurFileIndex);
  438. StartCoroutine(mUpdateIEnumerator);
  439. }
  440. /// <summary>
  441. /// 退出游戏
  442. /// </summary>
  443. private void ExitGame()
  444. {
  445. Application.Quit();
  446. }
  447. /// <summary>
  448. /// 更新
  449. /// </summary>
  450. private void Update()
  451. {
  452. // 未启用更新
  453. if (mUpdateTime < 0)
  454. {
  455. return;
  456. }
  457. // 开始检测更新
  458. if (mClientUpdateFileInfo != null && mServerUpdateFileInfo != null)
  459. {
  460. CheckUpdate();
  461. mUpdateTime = -1;
  462. }
  463. else
  464. {
  465. if (mUpdateTime + 10 <= Time.time)
  466. {
  467. mUpdateTime = -1;
  468. UI_CueDialog.Instance().Open("从服务器获取更新列表失败!请稍后重试!", "更新提示", E_DialogType.OneButton, ExitGame);
  469. }
  470. }
  471. }
  472. /// <summary>
  473. /// 协同加载 服务器上的更新文件信息
  474. /// </summary>
  475. /// <param name="url">地址</param>
  476. /// <param name="save">是否保存</param>
  477. /// <param name="fileInfo"> 更新文件信息</param>
  478. /// <returns>返回资源AssetBundle</returns>
  479. private IEnumerator IE_GetUpdateFile(string url, bool save, Action<UpdateFileInfo> fileInfo)
  480. {
  481. Debug.Log("获取配置的地址:" + url);
  482. //UnityWebRequest www = UnityWebRequest.Get(url);
  483. //yield return www.Send();
  484. // 是否有错误
  485. bool isError = false;
  486. // 上次的进度
  487. float lastProgress = 0;
  488. // 上次进度的时间
  489. float lastProgresstime = Time.time;
  490. WWW www = new WWW(url);
  491. // 未完成,更新进度
  492. while (www.isDone == false)
  493. {
  494. if (lastProgress == www.progress)
  495. {
  496. // 检测是否超时
  497. if (lastProgresstime + mTimeOut <= Time.time)
  498. {
  499. isError = true;
  500. break;
  501. }
  502. }
  503. else
  504. {
  505. mUpdateInfo.mCurFileProgress = www.progress + 0.05f;
  506. lastProgress = www.progress;
  507. lastProgresstime = Time.time;
  508. }
  509. yield return null;
  510. }
  511. // 未超时,但有网络返回的错误
  512. if (isError == false && string.IsNullOrEmpty(www.error) == false)
  513. {
  514. isError = true;
  515. }
  516. // 成功
  517. if (isError == false)
  518. {
  519. if (save)
  520. {
  521. mServerUpdateFileDatas = www.bytes;
  522. }
  523. byte[] datas = www.bytes;
  524. string DecrypText = System.Text.Encoding.UTF8.GetString(datas);
  525. fileInfo.Invoke(JsonUtility.FromJson<UpdateFileInfo>(DecrypText));
  526. }
  527. // 失败
  528. else
  529. {
  530. fileInfo(null);
  531. }
  532. www.Dispose();
  533. }
  534. /// <summary>
  535. /// 获取异步加载资源路径(用于 www 加载)
  536. /// </summary>
  537. /// <param name="strName">资源名称</param>
  538. /// <returns>返回实际路径</returns>
  539. public string GetPath(string strName)
  540. {
  541. string strPath = string.Empty;
  542. string persistentDataPath = resSaveRootPath + strName;
  543. if (Application.isEditor)
  544. {
  545. if (File.Exists(persistentDataPath))
  546. {
  547. strPath = "file:///" + resSaveRootPath + strName;
  548. }
  549. else
  550. {
  551. strPath = "file://" + Application.streamingAssetsPath + "/" + strName;
  552. }
  553. }
  554. else
  555. {
  556. if (File.Exists(persistentDataPath))
  557. {
  558. strPath = "file://" + resSaveRootPath + strName;
  559. }
  560. else
  561. {
  562. if (RuntimePlatform.Android == Application.platform)
  563. {
  564. strPath = Application.streamingAssetsPath + "/" + strName;
  565. }
  566. else
  567. {
  568. strPath = "file://" + Application.streamingAssetsPath + "/" + strName;
  569. }
  570. }
  571. }
  572. return strPath;
  573. }
  574. }
  575. /// <summary>
  576. /// 更新信息
  577. /// </summary>
  578. public class UpdateInfo
  579. {
  580. /// <summary>
  581. /// 总共的文件数
  582. /// </summary>
  583. public int mTotalFileCount = 0;
  584. /// <summary>
  585. /// 当前文件索引
  586. /// </summary>
  587. public int mCurFileIndex = 0;
  588. private float _curFileProgress;
  589. /// <summary>
  590. /// 当前文件的索引
  591. /// </summary>
  592. public float mCurFileProgress
  593. {
  594. set
  595. {
  596. this._curFileProgress = value;
  597. if (_curFileProgress > 1.0f)
  598. {
  599. _curFileProgress = 1.0f;
  600. }
  601. }
  602. get { return _curFileProgress; }
  603. }
  604. /// <summary>
  605. /// 当前文件的名称
  606. /// </summary>
  607. public string mCurFlieName = "";
  608. /// <summary>
  609. /// 更新是否完成
  610. /// </summary>
  611. public bool mUpdateComplete = false;
  612. }