static bool isVersionValid(const string& version, const string& base) { GBVersion v = splitVersion(version); GBVersion b = splitVersion(base); if (v.version1 == b.version1) { if (v.version2 == b.version2) return v.version3 > b.version3; else return v.version2 > b.version2; } return false; }
GBUpdate::GBUpdate() { _isUpdateEnabled = false; _isAutoUpdate = false; _remoteVersion = ""; _localVersion = ""; _localExternalVersion = ""; _downloader.reset(new network::Downloader()); _downloader->onFileTaskSuccess = [this](const DownloadTask& task) { onDownloadSuccess(task); }; _downloader->onTaskError = [this](const DownloadTask& task, int errorCode, int errorCodeInternal, const std::string& errorStr) {onDownloadError(task, errorCode, errorCodeInternal, errorStr); }; DIRECTOR()->getEventDispatcher()->addCustomEventListener(GBUPDATE_EVENT_UPDATE_START, [this](EventCustom * pEvent){ for (map<string, GBUpdateInfo>::iterator it = _downloadList.begin(); it != _downloadList.end(); it++) { if (it->second.location.empty()) { GBVersion version = splitVersion(_remoteVersion); _downloader->createDownloadFileTask(StringUtils::format("%s/%d.%d/%s", _updateUrl.c_str(), version.version1, version.version2, it->first.c_str()), GBUtils::getExternalPath(it->first), it->first); return; } } DIRECTOR()->getEventDispatcher()->dispatchCustomEvent(GBUPDATE_EVENT_UPDATE_FINISH, (void *)0); }); DIRECTOR()->getEventDispatcher()->addCustomEventListener(GBUPDATE_EVENT_UPDATED_ONE, [this](EventCustom * pEvent){ for (map<string, GBUpdateInfo>::iterator it = _downloadList.begin(); it != _downloadList.end(); it++) { if (it->second.location.empty()) { GBVersion version = splitVersion(_remoteVersion); _downloader->createDownloadFileTask(StringUtils::format("%s/%d.%d/%s", _updateUrl.c_str(), version.version1, version.version2, it->first.c_str()), GBUtils::getExternalPath(it->first), it->first); return; } } DIRECTOR()->getEventDispatcher()->dispatchCustomEvent(GBUPDATE_EVENT_UPDATE_FINISH, (void *)1); }); DIRECTOR()->getEventDispatcher()->addCustomEventListener(GBUPDATE_EVENT_UPDATE_FINISH, [this](EventCustom * pEvent){ long isDownloadFinish = (long)pEvent->getUserData(); if(isDownloadFinish) parseMD5(); }); }
void GBUpdate::requestRemoteVersion() { GBVersion version = splitVersion(getLocalVersion()); auto request = new (std::nothrow) HttpRequest(); request->setUrl(StringUtils::format("%s/%d.%d/md5", _updateUrl.c_str(), version.version1, version.version2).c_str()); request->setRequestType(HttpRequest::Type::GET); request->setResponseCallback(CC_CALLBACK_2(GBUpdate::onRequestVersionCompleted, this)); HttpClient::getInstance()->sendImmediate(request); request->release(); }
void GBUpdate::onRequestVersionCompleted(HttpClient *sender, HttpResponse *response) { do { if (!response) break; if (!response->isSucceed()) break; vector<char> * pBuf = response->getResponseData(); if (pBuf->size() > 0) { vector<vector<string>> v = GBCsv::parseCsv((unsigned char *)pBuf->data(), pBuf->size()); _remoteVersion = v[0][0]; } if (!_remoteVersion.empty()) { string localVersion = getLocalVersion(); if (_remoteVersion == localVersion) { if (getDownloadTotalSize() > MIN_DOWNLOAD_SIZE && !_isAutoUpdate) { DIRECTOR()->getEventDispatcher()->dispatchCustomEvent(GBUPDATE_EVENT_UPDATE_READY); } else { DIRECTOR()->getEventDispatcher()->dispatchCustomEvent(GBUPDATE_EVENT_UPDATE_START); } return; } else if (isVersionValid(_remoteVersion, getLocalVersion())) { GBVersion version = splitVersion(_remoteVersion); string md5Url = StringUtils::format("%s/%d.%d/md5", _updateUrl.c_str(), version.version1, version.version2); _downloader->createDownloadFileTask(md5Url, GBUtils::getExternalPath("md5"), "md5"); return; } } } while (false); DIRECTOR()->getEventDispatcher()->dispatchCustomEvent(GBUPDATE_EVENT_UPDATE_FINISH, (void *)0); }
void DLCchecking::update(entityx::ptr<entityx::EntityManager> es, entityx::ptr<entityx::EventManager> events, double dt) { for (auto entity : es->entities_with_components<Comp::DataVerCheck>()) { entityx::ptr<Comp::DataVerCheck> dllist = entity.component<Comp::DataVerCheck>(); std::string url = dllist->m_url; if (!dllist->m_verlist.empty()) { //we need to check if the version url is valid ( contains a manifest.json ) url += "/" + dllist->m_verlist.back() + "/manifest.json"; m_manifest = ""; CCLOG("DLCchecking reading from %s", url.c_str()); //list all files at given url CURLcode res; curl_easy_setopt(_curl, CURLOPT_URL, url.c_str()); #ifdef _DEBUG curl_easy_setopt(_curl, CURLOPT_VERBOSE, 1L); #endif curl_easy_setopt(_curl, CURLOPT_WRITEFUNCTION, write_data); curl_easy_setopt(_curl, CURLOPT_WRITEDATA, &m_manifest); if (_connectionTimeout) curl_easy_setopt(_curl, CURLOPT_CONNECTTIMEOUT, _connectionTimeout); curl_easy_setopt(_curl, CURLOPT_NOSIGNAL, 1L); curl_easy_setopt(_curl, CURLOPT_LOW_SPEED_LIMIT, LOW_SPEED_LIMIT); curl_easy_setopt(_curl, CURLOPT_LOW_SPEED_TIME, LOW_SPEED_TIME); res = curl_easy_perform(_curl); CCLOG("DLCchecking read from %s", url.c_str()); if (res != 0) { CCLOG("DLCchecking can not read from %s, error code is %d", url.c_str(), res); dllist->m_retries--; if (0 == dllist->m_retries) { CCLOGERROR("DLCchecking can not read from %s, error code is %d", url.c_str(), res); //signal error events->emit<Events::Error>(entity, "DLCchecking system"); //we give up on this entity entity.destroy(); } } else { //read the downloaded manifest to compare md5 rapidjson::Document json; json.Parse<0>(m_manifest.c_str()); if (json.HasParseError()) { CCLOG("GetParseError %s\n", json.GetParseError()); //version will be removed from the list later. } else { //is it possible to update ? bool dlc_update_allowed = false; //do we need to update ? bool dlc_update_required = false; std::string version = cocostudio::DictionaryHelper::getInstance()->getStringValue_json(json, "version", ""); if (version == dllist->m_current_version) //if we have the exact same string : developer update or current version hotfix. { dlc_update_required = true; } else if (version.length() > 0 ) //we need to compare string to find if the online version is more recent { unsigned long lver = 0; try { lver = ToolBox::stoul(version); CCLOG("DLC at %s has Manifest version %lu", url.c_str(), lver); } catch (std::out_of_range oor) { lver = 0; //disabling update if online version is too high ( developer version ) } unsigned long lcver = 0; try { lcver = ToolBox::stoul(dllist->m_current_version); CCLOG("Local DLC has Manifest version %lu", url.c_str(), lcver); } catch (std::out_of_range oor) { lcver = LONG_MAX; // if local version is too high, update should have been done before } if (lver > lcver) //not == to prevent LONG_MAX == LONG_MAX { dlc_update_required = true; } } std::string minAppVersion = cocostudio::DictionaryHelper::getInstance()->getStringValue_json(json, "minAppVersion", "error"); if (minAppVersion != "error" && dllist->m_current_minAppVersion != "" ) { //CAREFUL HERE with version comparison std::vector<unsigned long> mav = splitVersion(minAppVersion); std::vector<unsigned long> cmav = splitVersion(dllist->m_current_minAppVersion); dlc_update_allowed = true; while ( mav.size() < cmav.size() ) { mav.push_back(0); } for (unsigned int i = 0; i < cmav.size(); ++i) { if (mav.at(i) > cmav.at(i)) { dlc_update_allowed = false; break; } } }//if we cannot read the minimum app version. we dont download. better safe than sorry. if (dlc_update_allowed && dlc_update_required) { //prepare the list of downloads const rapidjson::Value & assets = cocostudio::DictionaryHelper::getInstance()->getSubDictionary_json(json, "assets"); for (rapidjson::Value::ConstMemberIterator m = assets.MemberonBegin(); m != assets.MemberonEnd(); ++m) { std::string filename = m->name.GetString(); std::string filehash = cocostudio::DictionaryHelper::getInstance()->getStringValue_json(m->value, "md5", "error"); //lowering filehash to be sure std::transform(filehash.begin(), filehash.end(), filehash.begin(), ::tolower); //std::cout << filename << " : " << filehash << std::endl; entityx::Entity newentity = es->create(); newentity.assign<Comp::LocalFile>(filename); newentity.assign<Comp::RemoteMD5>(filehash); newentity.assign<Comp::RemoteFile>(dllist->m_url + "/" + dllist->m_verlist.back(), filename); newentity.assign<Comp::ProgressValue>(1); } //downloading only the last verison should always be enough ( avoiding too many downloads - keeping all data for one version in same place ) //if (dllist->m_verlist.empty()) //if we checked all versions //{ entity.remove<Comp::DataVerCheck>(); //} //we dont need to do anything more with this entity entity.destroy(); } } //remove the version checked from the list dllist->m_verlist.pop_back(); //In case of error, we should check the next version in stack on next update. //If success this will not be done ( entity destroyed ) //TODO : check behavior } //exit this loop. one per update is enough break; } else //no version left to check { //we dont need to do anything more with this entity entity.destroy(); } } };