AssetsManagerEx.cpp 38 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200
  1. /****************************************************************************
  2. Copyright (c) 2014 cocos2d-x.org
  3. http://www.cocos2d-x.org
  4. Permission is hereby granted, free of charge, to any person obtaining a copy
  5. of this software and associated documentation files (the "Software"), to deal
  6. in the Software without restriction, including without limitation the rights
  7. to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  8. copies of the Software, and to permit persons to whom the Software is
  9. furnished to do so, subject to the following conditions:
  10. The above copyright notice and this permission notice shall be included in
  11. all copies or substantial portions of the Software.
  12. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  13. IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  14. FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  15. AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  16. LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  17. OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  18. THE SOFTWARE.
  19. ****************************************************************************/
  20. #include "AssetsManagerEx.h"
  21. #include "CCEventListenerAssetsManagerEx.h"
  22. #include "base/ccUTF8.h"
  23. #include "base/CCDirector.h"
  24. #include <stdio.h>
  25. #ifdef MINIZIP_FROM_SYSTEM
  26. #include <minizip/unzip.h>
  27. #else // from our embedded sources
  28. #include "unzip.h"
  29. #endif
  30. #include "base/CCAsyncTaskPool.h"
  31. NS_CC_EXT_BEGIN
  32. #define TEMP_PACKAGE_SUFFIX "_temp"
  33. #define VERSION_FILENAME "version.manifest"
  34. #define TEMP_MANIFEST_FILENAME "project.manifest.temp"
  35. #define MANIFEST_FILENAME "project.manifest"
  36. #define BUFFER_SIZE 8192
  37. #define MAX_FILENAME 512
  38. #define DEFAULT_CONNECTION_TIMEOUT 45
  39. #define SAVE_POINT_INTERVAL 0.1
  40. const std::string AssetsManagerEx::VERSION_ID = "@version";
  41. const std::string AssetsManagerEx::MANIFEST_ID = "@manifest";
  42. // Implementation of AssetsManagerEx
  43. AssetsManagerEx::AssetsManagerEx(const std::string& manifestUrl, const std::string& storagePath)
  44. : _updateState(State::UNCHECKED)
  45. , _assets(nullptr)
  46. , _storagePath("")
  47. , _tempVersionPath("")
  48. , _cacheManifestPath("")
  49. , _tempManifestPath("")
  50. , _manifestUrl(manifestUrl)
  51. , _localManifest(nullptr)
  52. , _tempManifest(nullptr)
  53. , _remoteManifest(nullptr)
  54. , _updateEntry(UpdateEntry::NONE)
  55. , _percent(0)
  56. , _percentByFile(0)
  57. , _totalToDownload(0)
  58. , _totalWaitToDownload(0)
  59. , _nextSavePoint(0.0)
  60. , _maxConcurrentTask(32)
  61. , _currConcurrentTask(0)
  62. , _versionCompareHandle(nullptr)
  63. , _verifyCallback(nullptr)
  64. , _inited(false)
  65. {
  66. // Init variables
  67. _eventDispatcher = Director::getInstance()->getEventDispatcher();
  68. std::string pointer = StringUtils::format("%p", this);
  69. _eventName = EventListenerAssetsManagerEx::LISTENER_ID + pointer;
  70. _fileUtils = FileUtils::getInstance();
  71. network::DownloaderHints hints =
  72. {
  73. static_cast<uint32_t>(_maxConcurrentTask),
  74. DEFAULT_CONNECTION_TIMEOUT,
  75. ".tmp"
  76. };
  77. _downloader = std::shared_ptr<network::Downloader>(new network::Downloader(hints));
  78. _downloader->onTaskError = std::bind(&AssetsManagerEx::onError, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4);
  79. _downloader->onTaskProgress = [this](const network::DownloadTask& task,
  80. int64_t /*bytesReceived*/,
  81. int64_t totalBytesReceived,
  82. int64_t totalBytesExpected)
  83. {
  84. this->onProgress(totalBytesExpected, totalBytesReceived, task.requestURL, task.identifier);
  85. };
  86. _downloader->onFileTaskSuccess = [this](const network::DownloadTask& task)
  87. {
  88. this->onSuccess(task.requestURL, task.storagePath, task.identifier);
  89. };
  90. setStoragePath(storagePath);
  91. _tempVersionPath = _tempStoragePath + VERSION_FILENAME;
  92. _cacheManifestPath = _storagePath + MANIFEST_FILENAME;
  93. _tempManifestPath = _tempStoragePath + TEMP_MANIFEST_FILENAME;
  94. initManifests(manifestUrl);
  95. }
  96. AssetsManagerEx::~AssetsManagerEx()
  97. {
  98. _downloader->onTaskError = (nullptr);
  99. _downloader->onFileTaskSuccess = (nullptr);
  100. _downloader->onTaskProgress = (nullptr);
  101. CC_SAFE_RELEASE(_localManifest);
  102. // _tempManifest could share a ptr with _remoteManifest or _localManifest
  103. if (_tempManifest != _localManifest && _tempManifest != _remoteManifest)
  104. CC_SAFE_RELEASE(_tempManifest);
  105. CC_SAFE_RELEASE(_remoteManifest);
  106. }
  107. AssetsManagerEx* AssetsManagerEx::create(const std::string& manifestUrl, const std::string& storagePath)
  108. {
  109. AssetsManagerEx* ret = new (std::nothrow) AssetsManagerEx(manifestUrl, storagePath);
  110. if (ret)
  111. {
  112. ret->autorelease();
  113. }
  114. else
  115. {
  116. CC_SAFE_DELETE(ret);
  117. }
  118. return ret;
  119. }
  120. void AssetsManagerEx::initManifests(const std::string& manifestUrl)
  121. {
  122. _inited = true;
  123. // Init and load local manifest
  124. _localManifest = new (std::nothrow) Manifest();
  125. if (_localManifest)
  126. {
  127. loadLocalManifest(manifestUrl);
  128. // Init and load temporary manifest
  129. _tempManifest = new (std::nothrow) Manifest();
  130. if (_tempManifest)
  131. {
  132. _tempManifest->parse(_tempManifestPath);
  133. // Previous update is interrupted
  134. if (_fileUtils->isFileExist(_tempManifestPath))
  135. {
  136. // Manifest parse failed, remove all temp files
  137. if (!_tempManifest->isLoaded())
  138. {
  139. _fileUtils->removeDirectory(_tempStoragePath);
  140. CC_SAFE_RELEASE(_tempManifest);
  141. _tempManifest = nullptr;
  142. }
  143. }
  144. }
  145. else
  146. {
  147. _inited = false;
  148. }
  149. // Init remote manifest for future usage
  150. _remoteManifest = new (std::nothrow) Manifest();
  151. if (!_remoteManifest)
  152. {
  153. _inited = false;
  154. }
  155. }
  156. else
  157. {
  158. _inited = false;
  159. }
  160. if (!_inited)
  161. {
  162. CC_SAFE_RELEASE(_localManifest);
  163. CC_SAFE_RELEASE(_tempManifest);
  164. CC_SAFE_RELEASE(_remoteManifest);
  165. _localManifest = nullptr;
  166. _tempManifest = nullptr;
  167. _remoteManifest = nullptr;
  168. }
  169. }
  170. void AssetsManagerEx::prepareLocalManifest()
  171. {
  172. // An alias to assets
  173. _assets = &(_localManifest->getAssets());
  174. // Add search paths
  175. _localManifest->prependSearchPaths();
  176. }
  177. void AssetsManagerEx::loadLocalManifest(const std::string& /*manifestUrl*/)
  178. {
  179. Manifest *cachedManifest = nullptr;
  180. // Find the cached manifest file
  181. if (_fileUtils->isFileExist(_cacheManifestPath))
  182. {
  183. cachedManifest = new (std::nothrow) Manifest();
  184. if (cachedManifest) {
  185. cachedManifest->parse(_cacheManifestPath);
  186. if (!cachedManifest->isLoaded())
  187. {
  188. _fileUtils->removeFile(_cacheManifestPath);
  189. CC_SAFE_RELEASE(cachedManifest);
  190. cachedManifest = nullptr;
  191. }
  192. }
  193. }
  194. // Ensure no search path of cached manifest is used to load this manifest
  195. std::vector<std::string> searchPaths = _fileUtils->getSearchPaths();
  196. if (cachedManifest)
  197. {
  198. std::vector<std::string> cacheSearchPaths = cachedManifest->getSearchPaths();
  199. std::vector<std::string> trimmedPaths = searchPaths;
  200. for (auto path : cacheSearchPaths)
  201. {
  202. const auto pos = std::find(trimmedPaths.begin(), trimmedPaths.end(), path);
  203. if (pos != trimmedPaths.end())
  204. {
  205. trimmedPaths.erase(pos);
  206. }
  207. }
  208. _fileUtils->setSearchPaths(trimmedPaths);
  209. }
  210. // Load local manifest in app package
  211. _localManifest->parse(_manifestUrl);
  212. if (cachedManifest) {
  213. // Restore search paths
  214. _fileUtils->setSearchPaths(searchPaths);
  215. }
  216. if (_localManifest->isLoaded())
  217. {
  218. // Compare with cached manifest to determine which one to use
  219. if (cachedManifest) {
  220. bool localNewer = _localManifest->versionGreater(cachedManifest, _versionCompareHandle);
  221. if (localNewer)
  222. {
  223. // Recreate storage, to empty the content
  224. _fileUtils->removeDirectory(_storagePath);
  225. _fileUtils->createDirectory(_storagePath);
  226. CC_SAFE_RELEASE(cachedManifest);
  227. }
  228. else
  229. {
  230. CC_SAFE_RELEASE(_localManifest);
  231. _localManifest = cachedManifest;
  232. }
  233. }
  234. prepareLocalManifest();
  235. }
  236. // Fail to load local manifest
  237. if (!_localManifest->isLoaded())
  238. {
  239. CCLOG("AssetsManagerEx : No local manifest file found error.\n");
  240. dispatchUpdateEvent(EventAssetsManagerEx::EventCode::ERROR_NO_LOCAL_MANIFEST);
  241. }
  242. }
  243. std::string AssetsManagerEx::basename(const std::string& path) const
  244. {
  245. size_t found = path.find_last_of("/\\");
  246. if (std::string::npos != found)
  247. {
  248. return path.substr(0, found);
  249. }
  250. else
  251. {
  252. return path;
  253. }
  254. }
  255. std::string AssetsManagerEx::get(const std::string& key) const
  256. {
  257. auto it = _assets->find(key);
  258. if (it != _assets->cend()) {
  259. return _storagePath + it->second.path;
  260. }
  261. else return "";
  262. }
  263. const Manifest* AssetsManagerEx::getLocalManifest() const
  264. {
  265. return _localManifest;
  266. }
  267. const Manifest* AssetsManagerEx::getRemoteManifest() const
  268. {
  269. return _remoteManifest;
  270. }
  271. const std::string& AssetsManagerEx::getStoragePath() const
  272. {
  273. return _storagePath;
  274. }
  275. void AssetsManagerEx::setStoragePath(const std::string& storagePath)
  276. {
  277. _storagePath = storagePath;
  278. adjustPath(_storagePath);
  279. _fileUtils->createDirectory(_storagePath);
  280. _tempStoragePath = _storagePath;
  281. _tempStoragePath.insert(_storagePath.size() - 1, TEMP_PACKAGE_SUFFIX);
  282. _fileUtils->createDirectory(_tempStoragePath);
  283. }
  284. void AssetsManagerEx::adjustPath(std::string &path)
  285. {
  286. if (path.size() > 0 && path[path.size() - 1] != '/')
  287. {
  288. path.append("/");
  289. }
  290. }
  291. bool AssetsManagerEx::decompress(const std::string &zip)
  292. {
  293. // Find root path for zip file
  294. size_t pos = zip.find_last_of("/\\");
  295. if (pos == std::string::npos)
  296. {
  297. CCLOG("AssetsManagerEx : no root path specified for zip file %s\n", zip.c_str());
  298. return false;
  299. }
  300. const std::string rootPath = zip.substr(0, pos+1);
  301. // Open the zip file
  302. unzFile zipfile = unzOpen(FileUtils::getInstance()->getSuitableFOpen(zip).c_str());
  303. if (! zipfile)
  304. {
  305. CCLOG("AssetsManagerEx : can not open downloaded zip file %s\n", zip.c_str());
  306. return false;
  307. }
  308. // Get info about the zip file
  309. unz_global_info global_info;
  310. if (unzGetGlobalInfo(zipfile, &global_info) != UNZ_OK)
  311. {
  312. CCLOG("AssetsManagerEx : can not read file global info of %s\n", zip.c_str());
  313. unzClose(zipfile);
  314. return false;
  315. }
  316. // Buffer to hold data read from the zip file
  317. char readBuffer[BUFFER_SIZE];
  318. // Loop to extract all files.
  319. uLong i;
  320. for (i = 0; i < global_info.number_entry; ++i)
  321. {
  322. // Get info about current file.
  323. unz_file_info fileInfo;
  324. char fileName[MAX_FILENAME];
  325. if (unzGetCurrentFileInfo(zipfile,
  326. &fileInfo,
  327. fileName,
  328. MAX_FILENAME,
  329. NULL,
  330. 0,
  331. NULL,
  332. 0) != UNZ_OK)
  333. {
  334. CCLOG("AssetsManagerEx : can not read compressed file info\n");
  335. unzClose(zipfile);
  336. return false;
  337. }
  338. const std::string fullPath = rootPath + fileName;
  339. // Check if this entry is a directory or a file.
  340. const size_t filenameLength = strlen(fileName);
  341. if (fileName[filenameLength-1] == '/')
  342. {
  343. //There are not directory entry in some case.
  344. //So we need to create directory when decompressing file entry
  345. if ( !_fileUtils->createDirectory(basename(fullPath)) )
  346. {
  347. // Failed to create directory
  348. CCLOG("AssetsManagerEx : can not create directory %s\n", fullPath.c_str());
  349. unzClose(zipfile);
  350. return false;
  351. }
  352. }
  353. else
  354. {
  355. // Create all directories in advance to avoid issue
  356. std::string dir = basename(fullPath);
  357. if (!_fileUtils->isDirectoryExist(dir)) {
  358. if (!_fileUtils->createDirectory(dir)) {
  359. // Failed to create directory
  360. CCLOG("AssetsManagerEx : can not create directory %s\n", fullPath.c_str());
  361. unzClose(zipfile);
  362. return false;
  363. }
  364. }
  365. // Entry is a file, so extract it.
  366. // Open current file.
  367. if (unzOpenCurrentFile(zipfile) != UNZ_OK)
  368. {
  369. CCLOG("AssetsManagerEx : can not extract file %s\n", fileName);
  370. unzClose(zipfile);
  371. return false;
  372. }
  373. // Create a file to store current file.
  374. FILE *out = fopen(FileUtils::getInstance()->getSuitableFOpen(fullPath).c_str(), "wb");
  375. if (!out)
  376. {
  377. CCLOG("AssetsManagerEx : can not create decompress destination file %s (errno: %d)\n", fullPath.c_str(), errno);
  378. unzCloseCurrentFile(zipfile);
  379. unzClose(zipfile);
  380. return false;
  381. }
  382. // Write current file content to destinate file.
  383. int error = UNZ_OK;
  384. do
  385. {
  386. error = unzReadCurrentFile(zipfile, readBuffer, BUFFER_SIZE);
  387. if (error < 0)
  388. {
  389. CCLOG("AssetsManagerEx : can not read zip file %s, error code is %d\n", fileName, error);
  390. fclose(out);
  391. unzCloseCurrentFile(zipfile);
  392. unzClose(zipfile);
  393. return false;
  394. }
  395. if (error > 0)
  396. {
  397. fwrite(readBuffer, error, 1, out);
  398. }
  399. } while(error > 0);
  400. fclose(out);
  401. }
  402. unzCloseCurrentFile(zipfile);
  403. // Goto next entry listed in the zip file.
  404. if ((i+1) < global_info.number_entry)
  405. {
  406. if (unzGoToNextFile(zipfile) != UNZ_OK)
  407. {
  408. CCLOG("AssetsManagerEx : can not read next file for decompressing\n");
  409. unzClose(zipfile);
  410. return false;
  411. }
  412. }
  413. }
  414. unzClose(zipfile);
  415. return true;
  416. }
  417. void AssetsManagerEx::decompressDownloadedZip(const std::string &customId, const std::string &storagePath)
  418. {
  419. struct AsyncData
  420. {
  421. std::string customId;
  422. std::string zipFile;
  423. bool succeed;
  424. };
  425. AsyncData* asyncData = new AsyncData;
  426. asyncData->customId = customId;
  427. asyncData->zipFile = storagePath;
  428. asyncData->succeed = false;
  429. std::function<void(void*)> decompressFinished = [this](void* param) {
  430. auto dataInner = reinterpret_cast<AsyncData*>(param);
  431. if (dataInner->succeed)
  432. {
  433. fileSuccess(dataInner->customId, dataInner->zipFile);
  434. }
  435. else
  436. {
  437. std::string errorMsg = "Unable to decompress file " + dataInner->zipFile;
  438. // Ensure zip file deletion (if decompress failure cause task thread exit anormally)
  439. _fileUtils->removeFile(dataInner->zipFile);
  440. dispatchUpdateEvent(EventAssetsManagerEx::EventCode::ERROR_DECOMPRESS, "", errorMsg);
  441. fileError(dataInner->customId, errorMsg);
  442. }
  443. delete dataInner;
  444. };
  445. AsyncTaskPool::getInstance()->enqueue(AsyncTaskPool::TaskType::TASK_OTHER, decompressFinished, (void*)asyncData, [this, asyncData]() {
  446. // Decompress all compressed files
  447. if (decompress(asyncData->zipFile))
  448. {
  449. asyncData->succeed = true;
  450. }
  451. _fileUtils->removeFile(asyncData->zipFile);
  452. });
  453. }
  454. void AssetsManagerEx::dispatchUpdateEvent(EventAssetsManagerEx::EventCode code, const std::string &assetId/* = ""*/, const std::string &message/* = ""*/, int curle_code/* = CURLE_OK*/, int curlm_code/* = CURLM_OK*/)
  455. {
  456. switch (code)
  457. {
  458. case EventAssetsManagerEx::EventCode::ERROR_UPDATING:
  459. case EventAssetsManagerEx::EventCode::ERROR_PARSE_MANIFEST:
  460. case EventAssetsManagerEx::EventCode::ERROR_NO_LOCAL_MANIFEST:
  461. case EventAssetsManagerEx::EventCode::ERROR_DECOMPRESS:
  462. case EventAssetsManagerEx::EventCode::ERROR_DOWNLOAD_MANIFEST:
  463. case EventAssetsManagerEx::EventCode::UPDATE_FAILED:
  464. case EventAssetsManagerEx::EventCode::UPDATE_FINISHED:
  465. case EventAssetsManagerEx::EventCode::ALREADY_UP_TO_DATE:
  466. _updateEntry = UpdateEntry::NONE;
  467. break;
  468. case EventAssetsManagerEx::EventCode::UPDATE_PROGRESSION:
  469. break;
  470. case EventAssetsManagerEx::EventCode::ASSET_UPDATED:
  471. break;
  472. case EventAssetsManagerEx::EventCode::NEW_VERSION_FOUND:
  473. if (_updateEntry == UpdateEntry::CHECK_UPDATE)
  474. {
  475. _updateEntry = UpdateEntry::NONE;
  476. }
  477. break;
  478. default:
  479. break;
  480. }
  481. EventAssetsManagerEx event(_eventName, this, code, _percent, _percentByFile, assetId, message, curle_code, curlm_code);
  482. _eventDispatcher->dispatchEvent(&event);
  483. }
  484. AssetsManagerEx::State AssetsManagerEx::getState() const
  485. {
  486. return _updateState;
  487. }
  488. void AssetsManagerEx::downloadVersion()
  489. {
  490. if (_updateState > State::PREDOWNLOAD_VERSION)
  491. return;
  492. std::string versionUrl = _localManifest->getVersionFileUrl();
  493. if (versionUrl.size() > 0)
  494. {
  495. _updateState = State::DOWNLOADING_VERSION;
  496. // Download version file asynchronously
  497. _downloader->createDownloadFileTask(versionUrl, _tempVersionPath, VERSION_ID);
  498. }
  499. // No version file found
  500. else
  501. {
  502. CCLOG("AssetsManagerEx : No version file found, step skipped\n");
  503. _updateState = State::PREDOWNLOAD_MANIFEST;
  504. downloadManifest();
  505. }
  506. }
  507. void AssetsManagerEx::parseVersion()
  508. {
  509. if (_updateState != State::VERSION_LOADED)
  510. return;
  511. _remoteManifest->parseVersion(_tempVersionPath);
  512. if (!_remoteManifest->isVersionLoaded())
  513. {
  514. CCLOG("AssetsManagerEx : Fail to parse version file, step skipped\n");
  515. _updateState = State::PREDOWNLOAD_MANIFEST;
  516. downloadManifest();
  517. }
  518. else
  519. {
  520. if (_localManifest->versionGreater(_remoteManifest, _versionCompareHandle))
  521. {
  522. _updateState = State::UP_TO_DATE;
  523. _fileUtils->removeDirectory(_tempStoragePath);
  524. dispatchUpdateEvent(EventAssetsManagerEx::EventCode::ALREADY_UP_TO_DATE);
  525. }
  526. else
  527. {
  528. _updateState = State::NEED_UPDATE;
  529. // Wait to update so continue the process
  530. if (_updateEntry == UpdateEntry::DO_UPDATE)
  531. {
  532. // dispatch after checking update entry because event dispatching may modify the update entry
  533. dispatchUpdateEvent(EventAssetsManagerEx::EventCode::NEW_VERSION_FOUND);
  534. _updateState = State::PREDOWNLOAD_MANIFEST;
  535. downloadManifest();
  536. }
  537. else
  538. {
  539. dispatchUpdateEvent(EventAssetsManagerEx::EventCode::NEW_VERSION_FOUND);
  540. }
  541. }
  542. }
  543. }
  544. void AssetsManagerEx::downloadManifest()
  545. {
  546. if (_updateState != State::PREDOWNLOAD_MANIFEST)
  547. return;
  548. std::string manifestUrl;
  549. if (_remoteManifest->isVersionLoaded()) {
  550. manifestUrl = _remoteManifest->getManifestFileUrl();
  551. } else {
  552. manifestUrl = _localManifest->getManifestFileUrl();
  553. }
  554. if (manifestUrl.size() > 0)
  555. {
  556. _updateState = State::DOWNLOADING_MANIFEST;
  557. // Download version file asynchronously
  558. _downloader->createDownloadFileTask(manifestUrl, _tempManifestPath, MANIFEST_ID);
  559. }
  560. // No manifest file found
  561. else
  562. {
  563. CCLOG("AssetsManagerEx : No manifest file found, check update failed\n");
  564. dispatchUpdateEvent(EventAssetsManagerEx::EventCode::ERROR_DOWNLOAD_MANIFEST);
  565. _updateState = State::UNCHECKED;
  566. }
  567. }
  568. void AssetsManagerEx::parseManifest()
  569. {
  570. if (_updateState != State::MANIFEST_LOADED)
  571. return;
  572. _remoteManifest->parse(_tempManifestPath);
  573. if (!_remoteManifest->isLoaded())
  574. {
  575. CCLOG("AssetsManagerEx : Error parsing manifest file, %s", _tempManifestPath.c_str());
  576. dispatchUpdateEvent(EventAssetsManagerEx::EventCode::ERROR_PARSE_MANIFEST);
  577. _updateState = State::UNCHECKED;
  578. }
  579. else
  580. {
  581. if (_localManifest->versionGreater(_remoteManifest, _versionCompareHandle))
  582. {
  583. _updateState = State::UP_TO_DATE;
  584. _fileUtils->removeDirectory(_tempStoragePath);
  585. dispatchUpdateEvent(EventAssetsManagerEx::EventCode::ALREADY_UP_TO_DATE);
  586. }
  587. else
  588. {
  589. _updateState = State::NEED_UPDATE;
  590. dispatchUpdateEvent(EventAssetsManagerEx::EventCode::NEW_VERSION_FOUND);
  591. if (_updateEntry == UpdateEntry::DO_UPDATE)
  592. {
  593. startUpdate();
  594. }
  595. }
  596. }
  597. }
  598. void AssetsManagerEx::startUpdate()
  599. {
  600. if (_updateState != State::NEED_UPDATE)
  601. return;
  602. _updateState = State::UPDATING;
  603. // Clean up before update
  604. _failedUnits.clear();
  605. _downloadUnits.clear();
  606. _totalWaitToDownload = _totalToDownload = 0;
  607. _nextSavePoint = 0;
  608. _percent = _percentByFile = _sizeCollected = _totalSize = 0;
  609. _downloadedSize.clear();
  610. _totalEnabled = false;
  611. // Temporary manifest exists, resuming previous download
  612. if (_tempManifest && _tempManifest->isLoaded() && _tempManifest->versionEquals(_remoteManifest))
  613. {
  614. _tempManifest->saveToFile(_tempManifestPath);
  615. _tempManifest->genResumeAssetsList(&_downloadUnits);
  616. _totalWaitToDownload = _totalToDownload = (int)_downloadUnits.size();
  617. this->batchDownload();
  618. std::string msg = StringUtils::format("Resuming from previous unfinished update, %d files remains to be finished.", _totalToDownload);
  619. dispatchUpdateEvent(EventAssetsManagerEx::EventCode::UPDATE_PROGRESSION, "", msg);
  620. }
  621. else
  622. {
  623. // Temporary manifest exists, but can't be parsed or version doesn't equals remote manifest (out of date)
  624. if (_tempManifest)
  625. {
  626. // Remove all temp files
  627. _fileUtils->removeDirectory(_tempStoragePath);
  628. CC_SAFE_RELEASE(_tempManifest);
  629. // Recreate temp storage path and save remote manifest
  630. _fileUtils->createDirectory(_tempStoragePath);
  631. _remoteManifest->saveToFile(_tempManifestPath);
  632. }
  633. // Temporary manifest will be used to register the download states of each asset,
  634. // in this case, it equals remote manifest.
  635. _tempManifest = _remoteManifest;
  636. // Check difference between local manifest and remote manifest
  637. std::unordered_map<std::string, Manifest::AssetDiff> diff_map = _localManifest->genDiff(_remoteManifest);
  638. if (diff_map.size() == 0)
  639. {
  640. updateSucceed();
  641. }
  642. else
  643. {
  644. // Generate download units for all assets that need to be updated or added
  645. std::string packageUrl = _remoteManifest->getPackageUrl();
  646. // Save current download manifest information for resuming
  647. _tempManifest->saveToFile(_tempManifestPath);
  648. // Preprocessing local files in previous version and creating download folders
  649. for (auto it = diff_map.begin(); it != diff_map.end(); ++it)
  650. {
  651. Manifest::AssetDiff diff = it->second;
  652. if (diff.type != Manifest::DiffType::DELETED)
  653. {
  654. std::string path = diff.asset.path;
  655. DownloadUnit unit;
  656. unit.customId = it->first;
  657. unit.srcUrl = packageUrl + path;
  658. unit.storagePath = _tempStoragePath + path;
  659. unit.size = diff.asset.size;
  660. _downloadUnits.emplace(unit.customId, unit);
  661. _tempManifest->setAssetDownloadState(it->first, Manifest::DownloadState::UNSTARTED);
  662. }
  663. }
  664. _totalWaitToDownload = _totalToDownload = (int)_downloadUnits.size();
  665. this->batchDownload();
  666. std::string msg = StringUtils::format("Start to update %d files from remote package.", _totalToDownload);
  667. dispatchUpdateEvent(EventAssetsManagerEx::EventCode::UPDATE_PROGRESSION, "", msg);
  668. }
  669. }
  670. }
  671. void AssetsManagerEx::updateSucceed()
  672. {
  673. // Every thing is correctly downloaded, do the following
  674. // 1. rename temporary manifest to valid manifest
  675. std::string tempFileName = TEMP_MANIFEST_FILENAME;
  676. std::string fileName = MANIFEST_FILENAME;
  677. _fileUtils->renameFile(_tempStoragePath, tempFileName, fileName);
  678. // 2. merge temporary storage path to storage path so that temporary version turns to cached version
  679. if (_fileUtils->isDirectoryExist(_tempStoragePath))
  680. {
  681. // Merging all files in temp storage path to storage path
  682. std::vector<std::string> files;
  683. _fileUtils->listFilesRecursively(_tempStoragePath, &files);
  684. int baseOffset = (int)_tempStoragePath.length();
  685. std::string relativePath, dstPath;
  686. for (std::vector<std::string>::iterator it = files.begin(); it != files.end(); ++it)
  687. {
  688. relativePath.assign((*it).substr(baseOffset));
  689. dstPath.assign(_storagePath + relativePath);
  690. // Create directory
  691. if (relativePath.back() == '/')
  692. {
  693. _fileUtils->createDirectory(dstPath);
  694. }
  695. // Copy file
  696. else
  697. {
  698. if (_fileUtils->isFileExist(dstPath))
  699. {
  700. _fileUtils->removeFile(dstPath);
  701. }
  702. _fileUtils->renameFile(*it, dstPath);
  703. }
  704. }
  705. // Remove temp storage path
  706. _fileUtils->removeDirectory(_tempStoragePath);
  707. }
  708. // 3. swap the localManifest
  709. CC_SAFE_RELEASE(_localManifest);
  710. _localManifest = _remoteManifest;
  711. _localManifest->setManifestRoot(_storagePath);
  712. _remoteManifest = nullptr;
  713. // 4. make local manifest take effect
  714. prepareLocalManifest();
  715. // 5. Set update state
  716. _updateState = State::UP_TO_DATE;
  717. // 6. Notify finished event
  718. dispatchUpdateEvent(EventAssetsManagerEx::EventCode::UPDATE_FINISHED);
  719. }
  720. void AssetsManagerEx::checkUpdate()
  721. {
  722. if (_updateEntry != UpdateEntry::NONE)
  723. {
  724. CCLOGERROR("AssetsManagerEx::checkUpdate, updateEntry isn't NONE");
  725. return;
  726. }
  727. if (!_inited){
  728. CCLOG("AssetsManagerEx : Manifests uninited.\n");
  729. dispatchUpdateEvent(EventAssetsManagerEx::EventCode::ERROR_NO_LOCAL_MANIFEST);
  730. return;
  731. }
  732. if (!_localManifest->isLoaded())
  733. {
  734. CCLOG("AssetsManagerEx : No local manifest file found error.\n");
  735. dispatchUpdateEvent(EventAssetsManagerEx::EventCode::ERROR_NO_LOCAL_MANIFEST);
  736. return;
  737. }
  738. _updateEntry = UpdateEntry::CHECK_UPDATE;
  739. switch (_updateState) {
  740. case State::UNCHECKED:
  741. case State::PREDOWNLOAD_VERSION:
  742. {
  743. downloadVersion();
  744. }
  745. break;
  746. case State::UP_TO_DATE:
  747. {
  748. dispatchUpdateEvent(EventAssetsManagerEx::EventCode::ALREADY_UP_TO_DATE);
  749. }
  750. break;
  751. case State::FAIL_TO_UPDATE:
  752. case State::NEED_UPDATE:
  753. {
  754. dispatchUpdateEvent(EventAssetsManagerEx::EventCode::NEW_VERSION_FOUND);
  755. }
  756. break;
  757. default:
  758. break;
  759. }
  760. }
  761. void AssetsManagerEx::update()
  762. {
  763. if (_updateEntry != UpdateEntry::NONE)
  764. {
  765. CCLOGERROR("AssetsManagerEx::update, updateEntry isn't NONE");
  766. return;
  767. }
  768. if (!_inited){
  769. CCLOG("AssetsManagerEx : Manifests uninited.\n");
  770. dispatchUpdateEvent(EventAssetsManagerEx::EventCode::ERROR_NO_LOCAL_MANIFEST);
  771. return;
  772. }
  773. if (!_localManifest->isLoaded())
  774. {
  775. CCLOG("AssetsManagerEx : No local manifest file found error.\n");
  776. dispatchUpdateEvent(EventAssetsManagerEx::EventCode::ERROR_NO_LOCAL_MANIFEST);
  777. return;
  778. }
  779. _updateEntry = UpdateEntry::DO_UPDATE;
  780. switch (_updateState) {
  781. case State::UNCHECKED:
  782. {
  783. _updateState = State::PREDOWNLOAD_VERSION;
  784. }
  785. case State::PREDOWNLOAD_VERSION:
  786. {
  787. downloadVersion();
  788. }
  789. break;
  790. case State::VERSION_LOADED:
  791. {
  792. parseVersion();
  793. }
  794. break;
  795. case State::PREDOWNLOAD_MANIFEST:
  796. {
  797. downloadManifest();
  798. }
  799. break;
  800. case State::MANIFEST_LOADED:
  801. {
  802. parseManifest();
  803. }
  804. break;
  805. case State::FAIL_TO_UPDATE:
  806. case State::NEED_UPDATE:
  807. {
  808. // Manifest not loaded yet
  809. if (!_remoteManifest->isLoaded())
  810. {
  811. _updateState = State::PREDOWNLOAD_MANIFEST;
  812. downloadManifest();
  813. }
  814. else
  815. {
  816. startUpdate();
  817. }
  818. }
  819. break;
  820. case State::UP_TO_DATE:
  821. case State::UPDATING:
  822. case State::UNZIPPING:
  823. _updateEntry = UpdateEntry::NONE;
  824. break;
  825. default:
  826. break;
  827. }
  828. }
  829. void AssetsManagerEx::updateAssets(const DownloadUnits& assets)
  830. {
  831. if (!_inited){
  832. CCLOG("AssetsManagerEx : Manifests uninited.\n");
  833. dispatchUpdateEvent(EventAssetsManagerEx::EventCode::ERROR_NO_LOCAL_MANIFEST);
  834. return;
  835. }
  836. if (_updateState != State::UPDATING && _localManifest->isLoaded() && _remoteManifest->isLoaded())
  837. {
  838. _updateState = State::UPDATING;
  839. _downloadUnits.clear();
  840. _downloadedSize.clear();
  841. _percent = _percentByFile = _sizeCollected = _totalSize = 0;
  842. _totalWaitToDownload = _totalToDownload = (int)assets.size();
  843. _nextSavePoint = 0;
  844. _totalEnabled = false;
  845. if (_totalToDownload > 0)
  846. {
  847. _downloadUnits = assets;
  848. this->batchDownload();
  849. }
  850. else if (_totalToDownload == 0)
  851. {
  852. onDownloadUnitsFinished();
  853. }
  854. }
  855. }
  856. const DownloadUnits& AssetsManagerEx::getFailedAssets() const
  857. {
  858. return _failedUnits;
  859. }
  860. void AssetsManagerEx::downloadFailedAssets()
  861. {
  862. CCLOG("AssetsManagerEx : Start update %lu failed assets.\n", static_cast<unsigned long>(_failedUnits.size()));
  863. updateAssets(_failedUnits);
  864. }
  865. void AssetsManagerEx::fileError(const std::string& identifier, const std::string& errorStr, int errorCode, int errorCodeInternal)
  866. {
  867. auto unitIt = _downloadUnits.find(identifier);
  868. // Found unit and add it to failed units
  869. if (unitIt != _downloadUnits.end())
  870. {
  871. _totalWaitToDownload--;
  872. DownloadUnit unit = unitIt->second;
  873. _failedUnits.emplace(unit.customId, unit);
  874. }
  875. dispatchUpdateEvent(EventAssetsManagerEx::EventCode::ERROR_UPDATING, identifier, errorStr, errorCode, errorCodeInternal);
  876. _tempManifest->setAssetDownloadState(identifier, Manifest::DownloadState::UNSTARTED);
  877. _currConcurrentTask = MAX(0, _currConcurrentTask-1);
  878. queueDowload();
  879. }
  880. void AssetsManagerEx::fileSuccess(const std::string &customId, const std::string &storagePath)
  881. {
  882. // Set download state to SUCCESSED
  883. _tempManifest->setAssetDownloadState(customId, Manifest::DownloadState::SUCCESSED);
  884. auto unitIt = _failedUnits.find(customId);
  885. // Found unit and delete it
  886. if (unitIt != _failedUnits.end())
  887. {
  888. // Remove from failed units list
  889. _failedUnits.erase(unitIt);
  890. }
  891. unitIt = _downloadUnits.find(customId);
  892. if (unitIt != _downloadUnits.end())
  893. {
  894. // Reduce count only when unit found in _downloadUnits
  895. _totalWaitToDownload--;
  896. _percentByFile = 100 * (float)(_totalToDownload - _totalWaitToDownload) / _totalToDownload;
  897. // Notify progression event
  898. dispatchUpdateEvent(EventAssetsManagerEx::EventCode::UPDATE_PROGRESSION, "");
  899. }
  900. // Notify asset updated event
  901. dispatchUpdateEvent(EventAssetsManagerEx::EventCode::ASSET_UPDATED, customId);
  902. _currConcurrentTask = MAX(0, _currConcurrentTask-1);
  903. queueDowload();
  904. }
  905. void AssetsManagerEx::onError(const network::DownloadTask& task,
  906. int errorCode,
  907. int errorCodeInternal,
  908. const std::string& errorStr)
  909. {
  910. // Skip version error occurred
  911. if (task.identifier == VERSION_ID)
  912. {
  913. CCLOG("AssetsManagerEx : Fail to download version file, step skipped\n");
  914. _updateState = State::PREDOWNLOAD_MANIFEST;
  915. downloadManifest();
  916. }
  917. else if (task.identifier == MANIFEST_ID)
  918. {
  919. dispatchUpdateEvent(EventAssetsManagerEx::EventCode::ERROR_DOWNLOAD_MANIFEST, task.identifier, errorStr, errorCode, errorCodeInternal);
  920. _updateState = State::FAIL_TO_UPDATE;
  921. }
  922. else
  923. {
  924. fileError(task.identifier, errorStr, errorCode, errorCodeInternal);
  925. }
  926. }
  927. void AssetsManagerEx::onProgress(double total, double downloaded, const std::string& /*url*/, const std::string &customId)
  928. {
  929. if (customId == VERSION_ID || customId == MANIFEST_ID)
  930. {
  931. _percent = 100 * downloaded / total;
  932. // Notify progression event
  933. dispatchUpdateEvent(EventAssetsManagerEx::EventCode::UPDATE_PROGRESSION, customId);
  934. return;
  935. }
  936. else
  937. {
  938. // Calcul total downloaded
  939. bool found = false;
  940. double totalDownloaded = 0;
  941. for (auto it = _downloadedSize.begin(); it != _downloadedSize.end(); ++it)
  942. {
  943. if (it->first == customId)
  944. {
  945. it->second = downloaded;
  946. found = true;
  947. }
  948. totalDownloaded += it->second;
  949. }
  950. // Collect information if not registed
  951. if (!found)
  952. {
  953. // Set download state to DOWNLOADING, this will run only once in the download process
  954. _tempManifest->setAssetDownloadState(customId, Manifest::DownloadState::DOWNLOADING);
  955. // Register the download size information
  956. _downloadedSize.emplace(customId, downloaded);
  957. // Check download unit size existance, if not exist collect size in total size
  958. if (_downloadUnits[customId].size == 0)
  959. {
  960. _totalSize += total;
  961. _sizeCollected++;
  962. // All collected, enable total size
  963. if (_sizeCollected == _totalToDownload)
  964. {
  965. _totalEnabled = true;
  966. }
  967. }
  968. }
  969. if (_totalEnabled && _updateState == State::UPDATING)
  970. {
  971. float currentPercent = 100 * totalDownloaded / _totalSize;
  972. // Notify at integer level change
  973. if ((int)currentPercent != (int)_percent) {
  974. _percent = currentPercent;
  975. // Notify progression event
  976. dispatchUpdateEvent(EventAssetsManagerEx::EventCode::UPDATE_PROGRESSION, customId);
  977. }
  978. }
  979. }
  980. }
  981. void AssetsManagerEx::onSuccess(const std::string &/*srcUrl*/, const std::string &storagePath, const std::string &customId)
  982. {
  983. if (customId == VERSION_ID)
  984. {
  985. _updateState = State::VERSION_LOADED;
  986. parseVersion();
  987. }
  988. else if (customId == MANIFEST_ID)
  989. {
  990. _updateState = State::MANIFEST_LOADED;
  991. parseManifest();
  992. }
  993. else
  994. {
  995. bool ok = true;
  996. auto &assets = _remoteManifest->getAssets();
  997. auto assetIt = assets.find(customId);
  998. if (assetIt != assets.end())
  999. {
  1000. Manifest::Asset asset = assetIt->second;
  1001. if (_verifyCallback != nullptr)
  1002. {
  1003. ok = _verifyCallback(storagePath, asset);
  1004. }
  1005. }
  1006. if (ok)
  1007. {
  1008. bool compressed = assetIt != assets.end() ? assetIt->second.compressed : false;
  1009. if (compressed)
  1010. {
  1011. decompressDownloadedZip(customId, storagePath);
  1012. }
  1013. else
  1014. {
  1015. fileSuccess(customId, storagePath);
  1016. }
  1017. }
  1018. else
  1019. {
  1020. fileError(customId, "Asset file verification failed after downloaded");
  1021. }
  1022. }
  1023. }
  1024. void AssetsManagerEx::destroyDownloadedVersion()
  1025. {
  1026. _fileUtils->removeDirectory(_storagePath);
  1027. _fileUtils->removeDirectory(_tempStoragePath);
  1028. }
  1029. void AssetsManagerEx::batchDownload()
  1030. {
  1031. _queue.clear();
  1032. for(auto iter : _downloadUnits)
  1033. {
  1034. const DownloadUnit& unit = iter.second;
  1035. if (unit.size > 0)
  1036. {
  1037. _totalSize += unit.size;
  1038. _sizeCollected++;
  1039. }
  1040. _queue.push_back(iter.first);
  1041. }
  1042. // All collected, enable total size
  1043. if (_sizeCollected == _totalToDownload)
  1044. {
  1045. _totalEnabled = true;
  1046. }
  1047. queueDowload();
  1048. }
  1049. void AssetsManagerEx::queueDowload()
  1050. {
  1051. if (_totalWaitToDownload == 0)
  1052. {
  1053. this->onDownloadUnitsFinished();
  1054. return;
  1055. }
  1056. while (_currConcurrentTask < _maxConcurrentTask && _queue.size() > 0)
  1057. {
  1058. std::string key = _queue.back();
  1059. _queue.pop_back();
  1060. _currConcurrentTask++;
  1061. DownloadUnit& unit = _downloadUnits[key];
  1062. _fileUtils->createDirectory(basename(unit.storagePath));
  1063. _downloader->createDownloadFileTask(unit.srcUrl, unit.storagePath, unit.customId);
  1064. _tempManifest->setAssetDownloadState(key, Manifest::DownloadState::DOWNLOADING);
  1065. }
  1066. if (_percentByFile / 100 > _nextSavePoint)
  1067. {
  1068. // Save current download manifest information for resuming
  1069. _tempManifest->saveToFile(_tempManifestPath);
  1070. _nextSavePoint += SAVE_POINT_INTERVAL;
  1071. }
  1072. }
  1073. void AssetsManagerEx::onDownloadUnitsFinished()
  1074. {
  1075. // Finished with error check
  1076. if (_failedUnits.size() > 0)
  1077. {
  1078. // Save current download manifest information for resuming
  1079. _tempManifest->saveToFile(_tempManifestPath);
  1080. _updateState = State::FAIL_TO_UPDATE;
  1081. dispatchUpdateEvent(EventAssetsManagerEx::EventCode::UPDATE_FAILED);
  1082. }
  1083. else if (_updateState == State::UPDATING)
  1084. {
  1085. updateSucceed();
  1086. }
  1087. }
  1088. NS_CC_EXT_END