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的更多相关文章

  1. SQL SERVER 2008 R2 SP1更新时,遇上共享功能更新失败解决方案

    SQL SERVER 2008 R2 SP1更新时,遇上共享功能更新失败的问题,可作如下尝试: 更新失败后,在windows的[事件查看器→应用程序]中找到来源为MsiInstaller,事件ID为1 ...

  2. Android SDK 更新时修改hosts文件仍然无法更新,可试试这个方法……

    Android SDK 更新时修改hosts文件仍然无法更新,此时必定万分蛋疼.在hosts文件中更换了各种ip,仍然解决不了!!!!!!!!!!!!!!? 第一步: 打开此软件,等待服务器连接 第二 ...

  3. 关于SVN更新时文件加锁的小结

    今天使用SVN更新应用,出现了下面的问题: update D:/workspace/acode/resource/springconf -r 6622 --force    Attempted to ...

  4. [Erlang10]为什么热更新时,Shell执行2次l(Module)后会把原来用到Module的进程 kill?

    0. 问题引入: -module(hot_code_server). -compile(export_all). start() –> erlang:register(?MODULE, erla ...

  5. Tomcat启动时项目反复载入,导致资源初始化两次的问题

    近期在项目开发測试的时候,发现Tomcat启动时项目反复载入,导致资源初始化两次的问题  导致该问题的解决办法: 例如以下图:在Eclipse中将Server Locations设置为"Us ...

  6. Windows远程时无法复制文件--杀进程rdpclip.exe,然后再启动

    1.远程登陆到主机上 2.任务管理器杀进程rdpclip.exe 3.[开始],搜索rdpclip.exe,点击运行 此时重新复制文件,可以跨主机复制啦 原以为是公司网络限制,现在看来还是没那么先进嘛

  7. mysql查询更新时的锁表机制分析

    为了给高并发情况下的mysql进行更好的优化,有必要了解一下mysql查询更新时的锁表机制. 一.概述 MySQL有三种锁的级别:页级.表级.行级.MyISAM和MEMORY存储引擎采用的是表级锁(t ...

  8. Postsharp 破解工具(通杀版,持续更新)

    2019.04.18 重要说明 VS2019 正式版已经发布了,Postsharp v6.2.2-Preview(预览版)也开始支持VS2019.不过截至目前,该预览版还不是特别稳定,因此提醒下大家在 ...

  9. mysql查询更新时的锁表机制分析(只介绍了MYISAM)

    为了给高并发情况下的mysql进行更好的优化,有必要了解一下mysql查询更新时的锁表机制. 一.概述 MySQL有三种锁的级别:页级.表级.行级.MyISAM和MEMORY存储引擎采用的是表级锁(t ...

随机推荐

  1. jmeter正则表达式提取器提取特定字符串后的全部内容

    jmeter进行JDBC请求时,请求后的响应结果在传递给下一个请求使用时,需要用到关联,也在jmeter中,关联通过正则表达式提取器实现. 但是,在JDBC请求后的响应结果中,往往需要关联的内容是只有 ...

  2. Java并发编程(三)-- 多线程的“问题”

    竞态条件与临界区 在同一程序中运行多个线程本身不会导致问题,问题在于多个线程访问了相同的资源.当多个线程同时访问同一个资源,并且其中的一个或者多个线程对这个资源进行了写操作,才会产生竞态条件.多个线程 ...

  3. CY7C68013 USB接口相机开发记录 - 第三天:固件修改

    上篇说了驱动怎么配置,这篇记录下对应的设备固件怎么配置.首先看下我们工程结构: 摘自官方文档AN61345 STARTUP.A51是建立工程时自动生成的文件,下面所有文件都是手动添加的.对于下面的几个 ...

  4. 树莓派mariadb折腾

    今天在树莓派之中安装mysql,结果被我安装了mariadb,这样做很讨厌,但是也可以将就用.记录一下折腾的过程. 安装就还是使用sudo apt install mysql 1.安装之后需要使用工具 ...

  5. px与rem的换算

    在线转化工具: http://www.ofmonkey.com/front/rem rem是相对于根元素<html>,这样就意味着,我们只需要在根元素确定一个参考值,这个参考值设置为多少, ...

  6. java第二周的学习知识

    1.java基本运行单位是类,类的组成成员为成员变量和方法.成员变量的种类有public,default(就是不写),protected,private.public:public可以修饰类,数据成员 ...

  7. nullptr/NULL

    NULL vs nullptr 在过去,我们如果要表示一个指针为空,我们条件反射肯定会这么写: int *p = NULL; 然而啊,有没有想过这是有问题的,比如下面的这段代码: #include & ...

  8. C/JS_二分法查找

    1. 二分法查找 前提: 数据是排好序的. 题设:给出一个有序arr,从中找出key,arr的区间是array[ low , higt]. 步骤: (1)mid=(low+high)/2 (2)arr ...

  9. 本地文件上传GitHub

    (1)mkdir 项目名称(2)cd  项目名称(3)git init 把它变成可管理的Git仓库(4)git status 查看状态(5)git add . 点用空格隔开(6)git status ...

  10. 【LCA】求和VII @北京OI2018

    目录 求和VII PROBLEM 题目描述 输入 输出 样例输入 样例输出 提示 SOLUTION CODE 求和VII PROBLEM 时间限制: 2 Sec 内存限制: 256 MB 题目描述 m ...