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 ...
随机推荐
- 解决win10 64位 + Python3 安装PyAutoIt报不是有效win32应用程序的办法
在win10 64位 + Python3的环境中,安装pyautoit第三方包的时候,报错提示为:不是有效win32应用程序.因为这个问题,进了python群,去寻求答案,却告知为,换32位的电脑,这 ...
- EF Core中的多对多映射如何实现?
EF 6.X中的多对多映射是直接使用HasMany-HasMany来做的.但是到了EF Core中,不再直接支持这种方式了,可以是可以使用,但是不推荐,具体使用可以参考<你必须掌握的Entity ...
- padding和margin设置成百分比
Margin和Padding是我们在网页设计经常使用到的CSS样式,他们分别是间距和填充,一个作用于盒子外面,一个作用于盒子里面,默认的情况下,这些属性的值都会被计算在盒子的面积里面,在网页开发中的流 ...
- 使用 TestNG 测试的时候事务总是roll back的解决办法
为要测试方法加上 @Rollback(false) 注解
- Springboot2.x 启动报错:Bean named 'xxxService'... but was actually of type 'com.sun.proxy.$Proxy82'
Springboot 2.0.5 搭建一个新项目启动后报错:Bean named 'xxxService'... but was actually of type 'com.sun.proxy.$Pr ...
- CY7C68013 USB接口相机开发记录 - 第四天:上位机编写1
前面学习了USB相机硬件固件.设备驱动,可以实现USB设备识别.数据发送的功能.然后,非常重要的一部分,USB设备发出的数据,我要怎么接受,怎么查看发送的数据是否是正确的.网上百度了下,大部分人都使用 ...
- ubuntu下使用nvm安装nodejs
sudo apt-get install curl curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.6/install. ...
- shell脚本使用--sleep
#!/bin/bash #filename.sh echo -n Count: tput sc count=; while true; do ]; then let count++; ; tput r ...
- Java集合框架(比较啰嗦)
阅读目录 概念与作用 集合框架的体系结构 Collection接口和List接口简介 Map和HashMap简介 集合工具类:Collections 小结 概念与作用 集合概念 现实生活中:很多事物凑 ...
- Jsp俩大内置对象学习
https://www.cnblogs.com/smyhvae/p/4065790.html post与get的区别 最直观的区别就是GET把参数包含在URL中,POST通过request body传 ...