Cocos 更新时反复杀进程,导致差异更新失效的Bug
Cocos 更新时反复杀进程时,差异更新失效的问题:
问题复现步骤:
1、在project.manifest.temp 文件下载成功后,下载Assets资源的时候杀掉进程
2、重启游戏,继续更新时会使用上次下载成功的project.manifest.temp文件,这个时候因为没有将文件下载状态保存,而更新的时候又判断没有下再成功就去下载,就导致将所有文件都下载了。
下载流程分析
1、 project.manifest.temp 文件下载成功之前如果kill进程,下次进入就会删除这个文件,重新下载;PS:调用update
接口开始更新时kill进程,再次启动都会引起project.manifest.temp的重新下载,这也是导致bug的原因之一。
// 这个方法会在更新之前调用
void AssetsManagerEx::initManifests(const std::string& manifestUrl)
{
_inited = true;
// Init and load local manifest
_localManifest = new (std::nothrow) Manifest();
if (_localManifest)
{
loadLocalManifest(manifestUrl);
// Init and load temporary manifest
_tempManifest = new (std::nothrow) Manifest();
if (_tempManifest)
{
_tempManifest->parse(_tempManifestPath);
// 这里判断如果文件不是完整的,并且存在就删除它;
if (!_tempManifest->isLoaded() && _fileUtils->isFileExist(_tempManifestPath))
_fileUtils->removeFile(_tempManifestPath);
}
else
{
_inited = false;
}
// Init remote manifest for future usage
_remoteManifest = new (std::nothrow) Manifest();
if (!_remoteManifest)
{
_inited = false;
}
}
else
{
_inited = false;
}
if (!_inited)
{
CC_SAFE_DELETE(_localManifest);
CC_SAFE_DELETE(_tempManifest);
CC_SAFE_DELETE(_remoteManifest);
}
}
2、开始下载根据project.manifest.temp 临时文件是否完整存在来决定是恢复之前的下载状态,还是根据重新下载回来的manifest文件与本地文件对比差异度,决定下载那些。
void AssetsManagerEx::startUpdate()
{
if (_updateState != State::NEED_UPDATE)
return;
_updateState = State::UPDATING;
// Clean up before update
_failedUnits.clear();
_downloadUnits.clear();
_compressedFiles.clear();
_totalWaitToDownload = _totalToDownload = 0;
_percent = _percentByFile = _sizeCollected = _totalSize = 0;
_downloadedSize.clear();
_totalEnabled = false;
// Temporary manifest exists, resuming previous download
if (_tempManifest->isLoaded() && _tempManifest->versionEquals(_remoteManifest))
{
// 文件是完整的,直接从文件恢复下载,并且从文件中读取下载状态,来判断是否需要下载
// 更新完成之前kill进程和每次启动程序下载project.manifest.temp文件都会导致下载状态被清空
// 恢复下载是根据manifest临时文件中保存的下载状态来决定,详细见3
_tempManifest->genResumeAssetsList(&_downloadUnits);
_totalWaitToDownload = _totalToDownload = (int)_downloadUnits.size();
this->batchDownload();
std::string msg = StringUtils::format("Resuming from previous unfinished update, %d files remains to be finished.", _totalToDownload);
CCLOG(msg);
// this time , the remoteManifest(has no DownloadState) file overwrite tempManifest file. when we kill process, and restart, it will uses error file.
// 为了避免被重新下载的manifest文件覆盖掉下载状态,我们这里需要将当前状态再次写入文件备份
_tempManifest->saveToFile(_tempManifestPath);
dispatchUpdateEvent(EventAssetsManagerEx::EventCode::UPDATE_PROGRESSION, "", msg);
}
// Check difference
else
{
// Temporary manifest not exists or out of date,
// it will be used to register the download states of each asset,
// in this case, it equals remote manifest.
_tempManifest->release();
_tempManifest = _remoteManifest;
std::unordered_map<std::string, Manifest::AssetDiff> diff_map = _localManifest->genDiff(_remoteManifest);
std::string log = StringUtils::format("AssetsManagerEx : Diff file size %d", diff_map.size());
CCLOG(log);
if (diff_map.size() == 0)
{
CCLOG("AssetsManagerEx updateSucceed");
updateSucceed();
}
else
{
// Generate download units for all assets that need to be updated or added
std::string packageUrl = _remoteManifest->getPackageUrl();
for (auto it = diff_map.begin(); it != diff_map.end(); ++it)
{
Manifest::AssetDiff diff = it->second;
if (diff.type == Manifest::DiffType::DELETED)
{
CCLOG("AssetsManagerEx DELETED " + diff.asset.path);
_fileUtils->removeFile(_storagePath + diff.asset.path);
}
else
{
std::string path = diff.asset.path;
CCLOG("AssetsManagerEx " + diff.asset.path + " type "+ diff.type);
// Create path
_fileUtils->createDirectory(basename(_storagePath + path));
DownloadUnit unit;
unit.customId = it->first;
unit.srcUrl = packageUrl + path;
unit.storagePath = _storagePath + path;
_downloadUnits.emplace(unit.customId, unit);
}
}
// Set other assets' downloadState to SUCCESSED
auto &assets = _remoteManifest->getAssets();
for (auto it = assets.cbegin(); it != assets.cend(); ++it)
{
const std::string &key = it->first;
auto diffIt = diff_map.find(key);
if (diffIt == diff_map.end())
{
// 根据文件对比,将不需要下载的文件的状态设置为下载成功
_tempManifest->setAssetDownloadState(key, Manifest::DownloadState::SUCCESSED);
}
}
_totalWaitToDownload = _totalToDownload = (int)_downloadUnits.size();
this->batchDownload();
std::string msg = StringUtils::format("Start to update %d files from remote package.", _totalToDownload);
// 为了避免进程在更新完成之前被kill导致下载状态丢失,这里先保存文件,备份一次
_tempManifest->saveToFile(_tempManifestPath);
dispatchUpdateEvent(EventAssetsManagerEx::EventCode::UPDATE_PROGRESSION, "", msg);
}
}
}
3、恢复下载是,需要根据之前保存在临时文件中的下载状态来决定下载那些文件
void Manifest::genResumeAssetsList(DownloadUnits *units) const
{
for (auto it = _assets.begin(); it != _assets.end(); ++it)
{
Asset asset = it->second;
if (asset.downloadState != DownloadState::SUCCESSED)
{
DownloadUnit unit;
unit.customId = it->first;
unit.srcUrl = _packageUrl + asset.path;
unit.storagePath = _manifestRoot + asset.path;
units->emplace(unit.customId, unit);
}
}
}
Cocos 更新时反复杀进程,导致差异更新失效的Bug的更多相关文章
- SQL SERVER 2008 R2 SP1更新时,遇上共享功能更新失败解决方案
SQL SERVER 2008 R2 SP1更新时,遇上共享功能更新失败的问题,可作如下尝试: 更新失败后,在windows的[事件查看器→应用程序]中找到来源为MsiInstaller,事件ID为1 ...
- Android SDK 更新时修改hosts文件仍然无法更新,可试试这个方法……
Android SDK 更新时修改hosts文件仍然无法更新,此时必定万分蛋疼.在hosts文件中更换了各种ip,仍然解决不了!!!!!!!!!!!!!!? 第一步: 打开此软件,等待服务器连接 第二 ...
- 关于SVN更新时文件加锁的小结
今天使用SVN更新应用,出现了下面的问题: update D:/workspace/acode/resource/springconf -r 6622 --force Attempted to ...
- [Erlang10]为什么热更新时,Shell执行2次l(Module)后会把原来用到Module的进程 kill?
0. 问题引入: -module(hot_code_server). -compile(export_all). start() –> erlang:register(?MODULE, erla ...
- Tomcat启动时项目反复载入,导致资源初始化两次的问题
近期在项目开发測试的时候,发现Tomcat启动时项目反复载入,导致资源初始化两次的问题 导致该问题的解决办法: 例如以下图:在Eclipse中将Server Locations设置为"Us ...
- Windows远程时无法复制文件--杀进程rdpclip.exe,然后再启动
1.远程登陆到主机上 2.任务管理器杀进程rdpclip.exe 3.[开始],搜索rdpclip.exe,点击运行 此时重新复制文件,可以跨主机复制啦 原以为是公司网络限制,现在看来还是没那么先进嘛
- mysql查询更新时的锁表机制分析
为了给高并发情况下的mysql进行更好的优化,有必要了解一下mysql查询更新时的锁表机制. 一.概述 MySQL有三种锁的级别:页级.表级.行级.MyISAM和MEMORY存储引擎采用的是表级锁(t ...
- Postsharp 破解工具(通杀版,持续更新)
2019.04.18 重要说明 VS2019 正式版已经发布了,Postsharp v6.2.2-Preview(预览版)也开始支持VS2019.不过截至目前,该预览版还不是特别稳定,因此提醒下大家在 ...
- mysql查询更新时的锁表机制分析(只介绍了MYISAM)
为了给高并发情况下的mysql进行更好的优化,有必要了解一下mysql查询更新时的锁表机制. 一.概述 MySQL有三种锁的级别:页级.表级.行级.MyISAM和MEMORY存储引擎采用的是表级锁(t ...
随机推荐
- html5手势操作与多指操作封装与Canvas图片裁切实战
当前情况,移动端的开发占比越来越高,单指的拖拽触碰等操作是常规需要.特殊的多指操作与手势操作还需另做处理,而且还涉及到兼容性问题. // 屏幕上存在两根或两根以上的手指 时触发 仅IOS存在手势事件, ...
- javascript基础的查缺补漏
对象转基本类型 let a = { valueOf() { return 0; }, toString() { return '1'; }, [Symbol.toPrimitive]() { retu ...
- SpringBoot整合elasticsearch
在这一篇文章开始之前,你需要先安装一个ElasticSearch,如果你是mac或者linux可以参考https://www.jianshu.com/p/e47b451375ea,如果是windows ...
- GBT 33200-2016 社会治安综合治理 综治中心建设与管理规范 GBT 31000-2015 社会治安综合治理基础数据规范
阚总发的两个国标的标准文件, 看看里面对于数据和问题的分类等. 我们出统计分析,可以按照标准出各个大类小类的各种指标数据. 结合这几天给潍坊弄的12345的报告, 整理出一个可以结合吴中现有平台数据, ...
- BZOJ.2159.Crash的文明世界(斯特林数 树形DP)
BZOJ 洛谷 挺套路但并不难的一道题 \(Description\) 给定一棵\(n\)个点的树和\(K\),边权为\(1\).对于每个点\(x\),求\(S(x)=\sum_{i=1}^ndis( ...
- Java笔记(十九) 反射
反射 反射是在运行时获取类型的信息,再根据这些信息进行操作. 一.Class类 每个已加载的类在内存中都有一份类信息,每个对象都有指向它的类信息的引用. 在Java中,类信息对应的类就是java.la ...
- 第一本docker书,,持续更新中
1.查看应用是否在docker中部署成功 需要确认curl已安装 whereis curl sudo apt-get -y install curl curl localhost:8081 如果成 ...
- mongoDB,mongoose,没有数组就添加,如果有了数组,就向数组中添加新元素
db.getCollection('photos').findOneAndUpdate("5b028e71f32bd5004f905879", //findByIdAndUpd ...
- python网络编程(二)
UDP介绍 UDP --- 用户数据报协议,是一个无连接的简单的面向数据报的运输层协议.UDP不提供可靠性,它只是把应用程序传给IP层的数据报发送出去,但是并不能保证它们能到达目的地.由于UDP在传输 ...
- bzoj1708:[Usaco2007 Oct]Money奶牛的硬币(完全背包
1708: [Usaco2007 Oct]Money奶牛的硬币 Time Limit: 5 Sec Memory Limit: 64 MBSubmit: 797 Solved: 540[Submi ...