manifest解析
里面牵扯到了对manifest的数据解析,发现的其他字段:
- groupVersions
- updating
- engineVersion
assets的其他字段
- downloadState
- compressed
- path
能不能使用2个参数的构造函数
可以,但是js层这样就拿不到版本号了,目前也没有其他方式让js层知道版本号信息,这是一个比较蛋疼的设计。
默认的比较版本函数
static int cmpVersion(const std::string& v1, const std::string& v2) { int i; int oct_v1[4] = {0}, oct_v2[4] = {0}; int filled1 = std::sscanf(v1.c_str(), "%d.%d.%d.%d", &oct_v1[0], &oct_v1[1], &oct_v1[2], &oct_v1[3]); int filled2 = std::sscanf(v2.c_str(), "%d.%d.%d.%d", &oct_v2[0], &oct_v2[1], &oct_v2[2], &oct_v2[3]); if (filled1 == 0 || filled2 == 0) { return strcmp(v1.c_str(), v2.c_str()); } for (i = 0; i < 4; i++) { if (oct_v1[i] > oct_v2[i]) return 1; else if (oct_v1[i] < oct_v2[i]) return -1; } return 0; }
回调函数
_versionCompareHandle
int versionCompareHandle (string versionLocal, string versionRemote){}
- 0 (false)表示
versionLocal < versionRemote
,即会触发热更新 - !0 (true) 表示
versionLocal >= versionRemote
,即不需要热更新,会触发ALREADY_UP_TO_DATE
事件
setVerifyCallback
对照c++,js能看的更加明白。
struct ManifestAsset { std::string md5; std::string path; bool compressed; float size; int downloadState; }; typedef ManifestAsset Asset; typedef std::function<bool(const std::string& path, Manifest::Asset asset)> VerifyCallback; void setVerifyCallback(const VerifyCallback& callback) {_verifyCallback = callback;};
this._assetsMgr.setVerifyCallback((assetsFullPath, asset) => { let {compressed, md5, path, size} = asset; if (compressed) { return true; } else { return true; } })
当下载一个文件后,会调用该回调,感觉设计上是希望用户作一些校验工作
void AssetsManagerEx::onSuccess(const std::string &/*srcUrl*/, const std::string &storagePath, const std::string &customId) { bool ok = true; auto &assets = _remoteManifest->getAssets(); auto assetIt = assets.find(customId); if (assetIt != assets.end()) { Manifest::Asset asset = assetIt->second; if (_verifyCallback != nullptr) { ok = _verifyCallback(storagePath, asset); } } if (ok) { bool compressed = assetIt != assets.end() ? assetIt->second.compressed : false; if (compressed) { decompressDownloadedZip(customId, storagePath); } else { fileSuccess(customId, storagePath); } } else { fileError(customId, "Asset file verification failed after downloaded"); } }
setEventCallback
热更新发生的异常,都会通过该回调函数通知到js层。 一般来说只需要设置一次就行了。
class CC_EX_DLL EventAssetsManagerEx : public cocos2d::Ref { public: //! Update events code enum class EventCode { ERROR_NO_LOCAL_MANIFEST, ERROR_DOWNLOAD_MANIFEST, ERROR_PARSE_MANIFEST, NEW_VERSION_FOUND, ALREADY_UP_TO_DATE, UPDATE_PROGRESSION, ASSET_UPDATED, ERROR_UPDATING, UPDATE_FINISHED, UPDATE_FAILED, ERROR_DECOMPRESS }; inline EventCode getEventCode() const { return _code; }; inline int getCURLECode() const { return _curle_code; }; inline int getCURLMCode() const { return _curlm_code; }; inline std::string getMessage() const { return _message; }; inline std::string getAssetId() const { return _assetId; }; inline cocos2d::extension::AssetsManagerEx *getAssetsManagerEx() const { return _manager; }; bool isResuming() const; float getPercent() const; float getPercentByFile() const; double getDownloadedBytes() const; double getTotalBytes() const; int getDownloadedFiles() const; int getTotalFiles() const; } typedef std::function<void(EventAssetsManagerEx *event)> EventCallback; void setEventCallback(const EventCallback& callback) {_eventCallback = callback;};
checkUpdate
void AssetsManagerEx::checkUpdate() { if (_updateEntry != UpdateEntry::NONE) { CCLOGERROR("AssetsManagerEx::checkUpdate, updateEntry isn't NONE"); return; } // 检查更新的时候会做一些基础的检查工作,比如本地的project.manifest是无效的,就会抛出异常: if (!_inited){ CCLOG("AssetsManagerEx : Manifests uninited.\n"); dispatchUpdateEvent(EventAssetsManagerEx::EventCode::ERROR_NO_LOCAL_MANIFEST); return; } if (!_localManifest->isLoaded()) { CCLOG("AssetsManagerEx : No local manifest file found error.\n"); dispatchUpdateEvent(EventAssetsManagerEx::EventCode::ERROR_NO_LOCAL_MANIFEST); return; } _updateEntry = UpdateEntry::CHECK_UPDATE; switch (_updateState) { case State::FAIL_TO_UPDATE: _updateState = State::UNCHECKED; case State::UNCHECKED: case State::PREDOWNLOAD_VERSION: { // 首先会尝试下载version.manifest downloadVersion(); } break; case State::UP_TO_DATE: { dispatchUpdateEvent(EventAssetsManagerEx::EventCode::ALREADY_UP_TO_DATE); } break; case State::NEED_UPDATE: { dispatchUpdateEvent(EventAssetsManagerEx::EventCode::NEW_VERSION_FOUND); } break; default: break; } } void AssetsManagerEx::downloadVersion() { // 获取version.manifest的url std::string versionUrl = _localManifest->getVersionFileUrl(); if (versionUrl.size() > 0) { _updateState = State::DOWNLOADING_VERSION; // 创建下载version.manifest的任务,当version文件下载下来后,就会比对版本号,如果需要热更就会继续去下载project.manifest文件 _downloader->createDownloadFileTask(versionUrl, _tempVersionPath, VERSION_ID); }else{ // 没有version.manifest字段的信息,就会去尝试下载project.manifest CCLOG("AssetsManagerEx : No version file found, step skipped\n"); _updateState = State::PREDOWNLOAD_MANIFEST; downloadManifest(); } } void AssetsManagerEx::downloadManifest() { // 获取project.version的地址 std::string manifestUrl = _localManifest->getManifestFileUrl(); if (manifestUrl.size() > 0) { _updateState = State::DOWNLOADING_MANIFEST; // 创建下载project.manifest的任务,当project.manifest下载完成后,会再次比对版本号 // 同时收集需要更新的文件大小数量等信息,但是这些信息js层无法拿到 // getTotalBytes getTotalFiles是挂在EventAssetsManagerEx身上 // 并派发事件,告诉js层需要更新 NEW_VERSION_FOUND _downloader->createDownloadFileTask(manifestUrl, _tempManifestPath, MANIFEST_ID); } else { // 如果project.manifest都没有,就会抛出异常 CCLOG("AssetsManagerEx : No manifest file found, check update failed\n"); dispatchUpdateEvent(EventAssetsManagerEx::EventCode::ERROR_DOWNLOAD_MANIFEST); _updateState = State::UNCHECKED; } }
下载的文件都会放到temp里面,接下来的事情就交给Downloader