• 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);
            }
        }
    }
    
  • 相关阅读:
    regsvr32 错误解决方案
    cefsharp解决闪烁
    WPF使用cefsharp 下载地址
    Winform下CefSharp的引用、配置、实例与报错排除(源码)
    cefSharp在XP下使得程序崩溃记录
    mvc3在window 7 iis7下以及 xp iis 5.1下的部署 ,asp.net MVC3无法打开项目文件E:/我们的项目/Project/HeatingMIS.Web/HeatingMIS.Web.csproj”。此安装不支持该项目类型。
    顺序程序设计
    你对linux了解多少,Linux 系统结构详解!
    算术运算符和算术表达式(优先级,结合性等)
    离散化和面元划分(可以理解为划分段)
  • 原文地址:https://www.cnblogs.com/lipeil/p/7360243.html
Copyright © 2020-2023  润新知