mirror of https://github.com/axmolengine/axmol.git
1304 lines
41 KiB
C++
1304 lines
41 KiB
C++
/****************************************************************************
|
|
Copyright (c) 2014 cocos2d-x.org
|
|
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
|
|
|
|
https://axys1.github.io/
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
of this software and associated documentation files (the "Software"), to deal
|
|
in the Software without restriction, including without limitation the rights
|
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
copies of the Software, and to permit persons to whom the Software is
|
|
furnished to do so, subject to the following conditions:
|
|
|
|
The above copyright notice and this permission notice shall be included in
|
|
all copies or substantial portions of the Software.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
THE SOFTWARE.
|
|
****************************************************************************/
|
|
#include "AssetsManagerEx.h"
|
|
#include "CCEventListenerAssetsManagerEx.h"
|
|
#include "base/ccUTF8.h"
|
|
#include "base/CCDirector.h"
|
|
|
|
#include <stdio.h>
|
|
|
|
#ifdef MINIZIP_FROM_SYSTEM
|
|
# include <minizip/unzip.h>
|
|
#else // from our embedded sources
|
|
# include "unzip.h"
|
|
#endif
|
|
#include <ioapi.h>
|
|
#include "base/CCAsyncTaskPool.h"
|
|
|
|
NS_AX_EXT_BEGIN
|
|
|
|
#define TEMP_FOLDERNAME "_temp"
|
|
#define VERSION_FILENAME "version.manifest"
|
|
#define TEMP_MANIFEST_FILENAME "project.manifest.temp"
|
|
#define MANIFEST_FILENAME "project.manifest"
|
|
|
|
#define BUFFER_SIZE 8192
|
|
#define MAX_FILENAME 512
|
|
|
|
#define DEFAULT_CONNECTION_TIMEOUT 45
|
|
|
|
#define SAVE_POINT_INTERVAL 0.1
|
|
|
|
const std::string AssetsManagerEx::VERSION_ID = "@version";
|
|
const std::string AssetsManagerEx::MANIFEST_ID = "@manifest";
|
|
|
|
class AssetManagerExZipFileInfo
|
|
{
|
|
public:
|
|
std::string zipFileName{};
|
|
};
|
|
|
|
// unzip overrides to support FileStream
|
|
long AssetManagerEx_tell_file_func(voidpf opaque, voidpf stream)
|
|
{
|
|
if (stream == nullptr)
|
|
return -1;
|
|
|
|
auto* fs = (FileStream*)stream;
|
|
|
|
return fs->tell();
|
|
}
|
|
|
|
long AssetManagerEx_seek_file_func(voidpf opaque, voidpf stream, uint32_t offset, int origin)
|
|
{
|
|
if (stream == nullptr)
|
|
return -1;
|
|
|
|
auto* fs = (FileStream*)stream;
|
|
|
|
return fs->seek((long)offset, origin); // must return 0 for success or -1 for error
|
|
}
|
|
|
|
voidpf AssetManagerEx_open_file_func(voidpf opaque, const char* filename, int mode)
|
|
{
|
|
FileStream::Mode fsMode;
|
|
if ((mode & ZLIB_FILEFUNC_MODE_READWRITEFILTER) == ZLIB_FILEFUNC_MODE_READ)
|
|
fsMode = FileStream::Mode::READ;
|
|
else if (mode & ZLIB_FILEFUNC_MODE_EXISTING)
|
|
fsMode = FileStream::Mode::APPEND;
|
|
else if (mode & ZLIB_FILEFUNC_MODE_CREATE)
|
|
fsMode = FileStream::Mode::WRITE;
|
|
else
|
|
return nullptr;
|
|
|
|
return FileUtils::getInstance()->openFileStream(filename, fsMode).release();
|
|
}
|
|
|
|
voidpf AssetManagerEx_opendisk_file_func(voidpf opaque, voidpf stream, uint32_t number_disk, int mode)
|
|
{
|
|
if (stream == nullptr)
|
|
return nullptr;
|
|
|
|
const auto zipFileInfo = static_cast<AssetManagerExZipFileInfo*>(opaque);
|
|
std::string diskFilename = zipFileInfo->zipFileName;
|
|
|
|
const auto pos = diskFilename.rfind('.', std::string::npos);
|
|
|
|
if (pos != std::string::npos && pos != 0)
|
|
{
|
|
const size_t bufferSize = 5;
|
|
char extensionBuffer[bufferSize];
|
|
snprintf(&extensionBuffer[0], bufferSize, ".z%02u", number_disk + 1);
|
|
diskFilename.replace(pos, std::min((size_t)4, zipFileInfo->zipFileName.size() - pos), extensionBuffer);
|
|
return AssetManagerEx_open_file_func(opaque, diskFilename.c_str(), mode);
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
uint32_t AssetManagerEx_read_file_func(voidpf opaque, voidpf stream, void* buf, uint32_t size)
|
|
{
|
|
if (stream == nullptr)
|
|
return (uint32_t)-1;
|
|
|
|
auto* fs = (FileStream*)stream;
|
|
return fs->read(buf, size);
|
|
}
|
|
|
|
uint32_t AssetManagerEx_write_file_func(voidpf opaque, voidpf stream, const void* buf, uint32_t size)
|
|
{
|
|
if (stream == nullptr)
|
|
return (uint32_t)-1;
|
|
|
|
auto* fs = (FileStream*)stream;
|
|
return fs->write(buf, size);
|
|
}
|
|
|
|
int AssetManagerEx_close_file_func(voidpf opaque, voidpf stream)
|
|
{
|
|
if (stream == nullptr)
|
|
return -1;
|
|
|
|
auto* fs = (FileStream*)stream;
|
|
const auto result = fs->close(); // 0 for success, -1 for error
|
|
delete fs;
|
|
return result;
|
|
}
|
|
|
|
// THis isn't supported by FileStream, so just check if the stream is null and open
|
|
int AssetManagerEx_error_file_func(voidpf opaque, voidpf stream)
|
|
{
|
|
if (stream == nullptr)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
auto* fs = (FileStream*)stream;
|
|
|
|
if (fs->isOpen())
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
// End of Overrides
|
|
|
|
// Implementation of AssetsManagerEx
|
|
|
|
AssetsManagerEx::AssetsManagerEx(std::string_view manifestUrl, std::string_view storagePath) : _manifestUrl(manifestUrl)
|
|
{
|
|
// Init variables
|
|
_eventDispatcher = Director::getInstance()->getEventDispatcher();
|
|
std::string pointer = StringUtils::format("%p", this);
|
|
_eventName = EventListenerAssetsManagerEx::LISTENER_ID + pointer;
|
|
_fileUtils = FileUtils::getInstance();
|
|
|
|
network::DownloaderHints hints = {static_cast<uint32_t>(_maxConcurrentTask), DEFAULT_CONNECTION_TIMEOUT, ".tmp"};
|
|
_downloader = std::shared_ptr<network::Downloader>(new network::Downloader(hints));
|
|
_downloader->onTaskError = std::bind(&AssetsManagerEx::onError, this, std::placeholders::_1, std::placeholders::_2,
|
|
std::placeholders::_3, std::placeholders::_4);
|
|
_downloader->onTaskProgress = [this](const network::DownloadTask& task) {
|
|
this->onProgress(task.progressInfo.totalBytesExpected, task.progressInfo.totalBytesReceived, task.requestURL,
|
|
task.identifier);
|
|
};
|
|
_downloader->onFileTaskSuccess = [this](const network::DownloadTask& task) {
|
|
this->onSuccess(task.requestURL, task.storagePath, task.identifier);
|
|
};
|
|
setStoragePath(storagePath);
|
|
_tempVersionPath = _tempStoragePath + VERSION_FILENAME;
|
|
_cacheManifestPath = _storagePath + MANIFEST_FILENAME;
|
|
_tempManifestPath = _tempStoragePath + TEMP_MANIFEST_FILENAME;
|
|
|
|
initManifests(manifestUrl);
|
|
}
|
|
|
|
AssetsManagerEx::~AssetsManagerEx()
|
|
{
|
|
_downloader->onTaskError = (nullptr);
|
|
_downloader->onFileTaskSuccess = (nullptr);
|
|
_downloader->onTaskProgress = (nullptr);
|
|
AX_SAFE_RELEASE(_localManifest);
|
|
// _tempManifest could share a ptr with _remoteManifest or _localManifest
|
|
if (_tempManifest != _localManifest && _tempManifest != _remoteManifest)
|
|
AX_SAFE_RELEASE(_tempManifest);
|
|
AX_SAFE_RELEASE(_remoteManifest);
|
|
}
|
|
|
|
AssetsManagerEx* AssetsManagerEx::create(std::string_view manifestUrl, std::string_view storagePath)
|
|
{
|
|
AssetsManagerEx* ret = new AssetsManagerEx(manifestUrl, storagePath);
|
|
ret->autorelease();
|
|
return ret;
|
|
}
|
|
|
|
void AssetsManagerEx::initManifests(std::string_view manifestUrl)
|
|
{
|
|
_inited = true;
|
|
// Init and load local manifest
|
|
_localManifest = new Manifest();
|
|
loadLocalManifest(manifestUrl);
|
|
|
|
// Init and load temporary manifest
|
|
_tempManifest = new Manifest();
|
|
_tempManifest->parse(_tempManifestPath);
|
|
// Previous update is interrupted
|
|
if (_fileUtils->isFileExist(_tempManifestPath))
|
|
{
|
|
// Manifest parse failed, remove all temp files
|
|
if (!_tempManifest->isLoaded())
|
|
{
|
|
_fileUtils->removeDirectory(_tempStoragePath);
|
|
AX_SAFE_RELEASE(_tempManifest);
|
|
_tempManifest = nullptr;
|
|
}
|
|
}
|
|
|
|
// Init remote manifest for future usage
|
|
_remoteManifest = new Manifest();
|
|
|
|
if (!_inited)
|
|
{
|
|
AX_SAFE_RELEASE(_localManifest);
|
|
AX_SAFE_RELEASE(_tempManifest);
|
|
AX_SAFE_RELEASE(_remoteManifest);
|
|
_localManifest = nullptr;
|
|
_tempManifest = nullptr;
|
|
_remoteManifest = nullptr;
|
|
}
|
|
}
|
|
|
|
void AssetsManagerEx::prepareLocalManifest()
|
|
{
|
|
// An alias to assets
|
|
_assets = &(_localManifest->getAssets());
|
|
|
|
// Add search paths
|
|
_localManifest->prependSearchPaths();
|
|
}
|
|
|
|
void AssetsManagerEx::loadLocalManifest(std::string_view /*manifestUrl*/)
|
|
{
|
|
Manifest* cachedManifest = nullptr;
|
|
// Find the cached manifest file
|
|
if (_fileUtils->isFileExist(_cacheManifestPath))
|
|
{
|
|
cachedManifest = new Manifest();
|
|
cachedManifest->parse(_cacheManifestPath);
|
|
if (!cachedManifest->isLoaded())
|
|
{
|
|
_fileUtils->removeFile(_cacheManifestPath);
|
|
AX_SAFE_RELEASE(cachedManifest);
|
|
cachedManifest = nullptr;
|
|
}
|
|
}
|
|
|
|
// Ensure no search path of cached manifest is used to load this manifest
|
|
std::vector<std::string> searchPaths = _fileUtils->getSearchPaths();
|
|
if (cachedManifest)
|
|
{
|
|
std::vector<std::string> cacheSearchPaths = cachedManifest->getSearchPaths();
|
|
std::vector<std::string> trimmedPaths = searchPaths;
|
|
for (const auto& path : cacheSearchPaths)
|
|
{
|
|
const auto pos = std::find(trimmedPaths.begin(), trimmedPaths.end(), path);
|
|
if (pos != trimmedPaths.end())
|
|
{
|
|
trimmedPaths.erase(pos);
|
|
}
|
|
}
|
|
_fileUtils->setSearchPaths(trimmedPaths);
|
|
}
|
|
// Load local manifest in app package
|
|
_localManifest->parse(_manifestUrl);
|
|
if (cachedManifest)
|
|
{
|
|
// Restore search paths
|
|
_fileUtils->setSearchPaths(searchPaths);
|
|
}
|
|
if (_localManifest->isLoaded())
|
|
{
|
|
// Compare with cached manifest to determine which one to use
|
|
if (cachedManifest)
|
|
{
|
|
bool localNewer = _localManifest->versionGreater(cachedManifest, _versionCompareHandle);
|
|
if (localNewer)
|
|
{
|
|
// Recreate storage, to empty the content
|
|
_fileUtils->removeDirectory(_storagePath);
|
|
_fileUtils->createDirectory(_storagePath);
|
|
AX_SAFE_RELEASE(cachedManifest);
|
|
}
|
|
else
|
|
{
|
|
AX_SAFE_RELEASE(_localManifest);
|
|
_localManifest = cachedManifest;
|
|
}
|
|
}
|
|
prepareLocalManifest();
|
|
}
|
|
|
|
// Fail to load local manifest
|
|
if (!_localManifest->isLoaded())
|
|
{
|
|
AXLOG("AssetsManagerEx : No local manifest file found error.\n");
|
|
dispatchUpdateEvent(EventAssetsManagerEx::EventCode::ERROR_NO_LOCAL_MANIFEST);
|
|
}
|
|
}
|
|
|
|
std::string_view AssetsManagerEx::basename(std::string_view path) const
|
|
{
|
|
size_t found = path.find_last_of("/\\");
|
|
|
|
if (std::string::npos != found)
|
|
{
|
|
return path.substr(0, found);
|
|
}
|
|
else
|
|
{
|
|
return path;
|
|
}
|
|
}
|
|
|
|
std::string AssetsManagerEx::get(std::string_view key) const
|
|
{
|
|
auto it = _assets->find(key);
|
|
if (it != _assets->cend())
|
|
{
|
|
return _storagePath + it->second.path;
|
|
}
|
|
else
|
|
return "";
|
|
}
|
|
|
|
const Manifest* AssetsManagerEx::getLocalManifest() const
|
|
{
|
|
return _localManifest;
|
|
}
|
|
|
|
const Manifest* AssetsManagerEx::getRemoteManifest() const
|
|
{
|
|
return _remoteManifest;
|
|
}
|
|
|
|
std::string_view AssetsManagerEx::getStoragePath() const
|
|
{
|
|
return _storagePath;
|
|
}
|
|
|
|
void AssetsManagerEx::setStoragePath(std::string_view storagePath)
|
|
{
|
|
_storagePath = storagePath;
|
|
adjustPath(_storagePath);
|
|
_fileUtils->createDirectory(_storagePath);
|
|
|
|
_tempStoragePath = _storagePath;
|
|
_tempStoragePath.append(TEMP_FOLDERNAME);
|
|
adjustPath(_tempStoragePath);
|
|
_fileUtils->createDirectory(_tempStoragePath);
|
|
}
|
|
|
|
void AssetsManagerEx::adjustPath(std::string& path)
|
|
{
|
|
if (!path.empty() && path[path.size() - 1] != '/')
|
|
{
|
|
path.push_back('/');
|
|
}
|
|
}
|
|
|
|
bool AssetsManagerEx::decompress(std::string_view zip)
|
|
{
|
|
// Find root path for zip file
|
|
size_t pos = zip.find_last_of("/\\");
|
|
if (pos == std::string::npos)
|
|
{
|
|
AXLOG("AssetsManagerEx : no root path specified for zip file %s\n", zip.data());
|
|
return false;
|
|
}
|
|
const std::string_view rootPath = zip.substr(0, pos + 1);
|
|
|
|
zlib_filefunc_def_s zipFunctionOverrides;
|
|
fillZipFunctionOverrides(zipFunctionOverrides);
|
|
|
|
AssetManagerExZipFileInfo zipFileInfo;
|
|
zipFileInfo.zipFileName = zip;
|
|
|
|
zipFunctionOverrides.opaque = &zipFileInfo;
|
|
|
|
// Open the zip file
|
|
unzFile zipfile = unzOpen2(zip.data(), &zipFunctionOverrides);
|
|
if (!zipfile)
|
|
{
|
|
AXLOG("AssetsManagerEx : can not open downloaded zip file %s\n", zip.data());
|
|
return false;
|
|
}
|
|
|
|
// Get info about the zip file
|
|
unz_global_info global_info;
|
|
if (unzGetGlobalInfo(zipfile, &global_info) != UNZ_OK)
|
|
{
|
|
AXLOG("AssetsManagerEx : can not read file global info of %s\n", zip.data());
|
|
unzClose(zipfile);
|
|
return false;
|
|
}
|
|
|
|
// Buffer to hold data read from the zip file
|
|
char readBuffer[BUFFER_SIZE];
|
|
// Loop to extract all files.
|
|
uLong i;
|
|
for (i = 0; i < global_info.number_entry; ++i)
|
|
{
|
|
// Get info about current file.
|
|
unz_file_info fileInfo;
|
|
char fileName[MAX_FILENAME];
|
|
if (unzGetCurrentFileInfo(zipfile, &fileInfo, fileName, MAX_FILENAME, NULL, 0, NULL, 0) != UNZ_OK)
|
|
{
|
|
AXLOG("AssetsManagerEx : can not read compressed file info\n");
|
|
unzClose(zipfile);
|
|
return false;
|
|
}
|
|
std::string fullPath{rootPath};
|
|
fullPath += fileName;
|
|
|
|
// Check if this entry is a directory or a file.
|
|
const size_t filenameLength = strlen(fileName);
|
|
if (fileName[filenameLength - 1] == '/')
|
|
{
|
|
// There are not directory entry in some case.
|
|
// So we need to create directory when decompressing file entry
|
|
if (!_fileUtils->createDirectory(basename(fullPath)))
|
|
{
|
|
// Failed to create directory
|
|
AXLOG("AssetsManagerEx : can not create directory %s\n", fullPath.c_str());
|
|
unzClose(zipfile);
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Create all directories in advance to avoid issue
|
|
std::string_view dir = basename(fullPath);
|
|
if (!_fileUtils->isDirectoryExist(dir))
|
|
{
|
|
if (!_fileUtils->createDirectory(dir))
|
|
{
|
|
// Failed to create directory
|
|
AXLOG("AssetsManagerEx : can not create directory %s\n", fullPath.c_str());
|
|
unzClose(zipfile);
|
|
return false;
|
|
}
|
|
}
|
|
// Entry is a file, so extract it.
|
|
// Open current file.
|
|
if (unzOpenCurrentFile(zipfile) != UNZ_OK)
|
|
{
|
|
AXLOG("AssetsManagerEx : can not extract file %s\n", fileName);
|
|
unzClose(zipfile);
|
|
return false;
|
|
}
|
|
|
|
// Create a file to store current file.
|
|
auto fsOut = FileUtils::getInstance()->openFileStream(fullPath, FileStream::Mode::WRITE);
|
|
if (!fsOut)
|
|
{
|
|
AXLOG("AssetsManagerEx : can not create decompress destination file %s (errno: %d)\n", fullPath.c_str(),
|
|
errno);
|
|
unzCloseCurrentFile(zipfile);
|
|
unzClose(zipfile);
|
|
return false;
|
|
}
|
|
|
|
// Write current file content to destinate file.
|
|
int error = UNZ_OK;
|
|
do
|
|
{
|
|
error = unzReadCurrentFile(zipfile, readBuffer, BUFFER_SIZE);
|
|
if (error < 0)
|
|
{
|
|
AXLOG("AssetsManagerEx : can not read zip file %s, error code is %d\n", fileName, error);
|
|
fsOut.reset();
|
|
unzCloseCurrentFile(zipfile);
|
|
unzClose(zipfile);
|
|
return false;
|
|
}
|
|
|
|
if (error > 0)
|
|
{
|
|
fsOut->write(readBuffer, error);
|
|
}
|
|
} while (error > 0);
|
|
|
|
fsOut.reset();
|
|
}
|
|
|
|
unzCloseCurrentFile(zipfile);
|
|
|
|
// Goto next entry listed in the zip file.
|
|
if ((i + 1) < global_info.number_entry)
|
|
{
|
|
if (unzGoToNextFile(zipfile) != UNZ_OK)
|
|
{
|
|
AXLOG("AssetsManagerEx : can not read next file for decompressing\n");
|
|
unzClose(zipfile);
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
unzClose(zipfile);
|
|
return true;
|
|
}
|
|
|
|
void AssetsManagerEx::decompressDownloadedZip(std::string_view customId, std::string_view storagePath)
|
|
{
|
|
struct AsyncData
|
|
{
|
|
std::string customId;
|
|
std::string zipFile;
|
|
bool succeed;
|
|
};
|
|
|
|
AsyncData* asyncData = new AsyncData;
|
|
asyncData->customId = customId;
|
|
asyncData->zipFile = storagePath;
|
|
asyncData->succeed = false;
|
|
|
|
std::function<void(void*)> decompressFinished = [this](void* param) {
|
|
auto dataInner = reinterpret_cast<AsyncData*>(param);
|
|
if (dataInner->succeed)
|
|
{
|
|
fileSuccess(dataInner->customId, dataInner->zipFile);
|
|
}
|
|
else
|
|
{
|
|
std::string errorMsg = "Unable to decompress file " + dataInner->zipFile;
|
|
// Ensure zip file deletion (if decompress failure cause task thread exit anormally)
|
|
_fileUtils->removeFile(dataInner->zipFile);
|
|
dispatchUpdateEvent(EventAssetsManagerEx::EventCode::ERROR_DECOMPRESS, "", errorMsg);
|
|
fileError(dataInner->customId, errorMsg);
|
|
}
|
|
delete dataInner;
|
|
};
|
|
AsyncTaskPool::getInstance()->enqueue(AsyncTaskPool::TaskType::TASK_OTHER, std::move(decompressFinished),
|
|
(void*)asyncData, [this, asyncData]() {
|
|
// Decompress all compressed files
|
|
if (decompress(asyncData->zipFile))
|
|
{
|
|
asyncData->succeed = true;
|
|
}
|
|
_fileUtils->removeFile(asyncData->zipFile);
|
|
});
|
|
}
|
|
|
|
void AssetsManagerEx::dispatchUpdateEvent(EventAssetsManagerEx::EventCode code,
|
|
std::string_view assetId /* = ""*/,
|
|
std::string_view message /* = ""*/,
|
|
int curle_code /* = CURLE_OK*/,
|
|
int curlm_code /* = CURLM_OK*/)
|
|
{
|
|
switch (code)
|
|
{
|
|
case EventAssetsManagerEx::EventCode::ERROR_UPDATING:
|
|
case EventAssetsManagerEx::EventCode::ERROR_PARSE_MANIFEST:
|
|
case EventAssetsManagerEx::EventCode::ERROR_NO_LOCAL_MANIFEST:
|
|
case EventAssetsManagerEx::EventCode::ERROR_DECOMPRESS:
|
|
case EventAssetsManagerEx::EventCode::ERROR_DOWNLOAD_MANIFEST:
|
|
case EventAssetsManagerEx::EventCode::UPDATE_FAILED:
|
|
case EventAssetsManagerEx::EventCode::UPDATE_FINISHED:
|
|
case EventAssetsManagerEx::EventCode::ALREADY_UP_TO_DATE:
|
|
_updateEntry = UpdateEntry::NONE;
|
|
break;
|
|
case EventAssetsManagerEx::EventCode::UPDATE_PROGRESSION:
|
|
break;
|
|
case EventAssetsManagerEx::EventCode::ASSET_UPDATED:
|
|
break;
|
|
case EventAssetsManagerEx::EventCode::NEW_VERSION_FOUND:
|
|
if (_updateEntry == UpdateEntry::CHECK_UPDATE)
|
|
{
|
|
_updateEntry = UpdateEntry::NONE;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
EventAssetsManagerEx event(_eventName, this, code, _percent, _percentByFile, assetId, message, curle_code,
|
|
curlm_code);
|
|
_eventDispatcher->dispatchEvent(&event);
|
|
}
|
|
|
|
AssetsManagerEx::State AssetsManagerEx::getState() const
|
|
{
|
|
return _updateState;
|
|
}
|
|
|
|
void AssetsManagerEx::downloadVersion()
|
|
{
|
|
if (_updateState > State::PREDOWNLOAD_VERSION)
|
|
return;
|
|
|
|
std::string_view versionUrl = _localManifest->getVersionFileUrl();
|
|
|
|
if (!versionUrl.empty())
|
|
{
|
|
_updateState = State::DOWNLOADING_VERSION;
|
|
// Download version file asynchronously
|
|
_downloader->createDownloadFileTask(versionUrl, _tempVersionPath, VERSION_ID);
|
|
}
|
|
// No version file found
|
|
else
|
|
{
|
|
AXLOG("AssetsManagerEx : No version file found, step skipped\n");
|
|
_updateState = State::PREDOWNLOAD_MANIFEST;
|
|
downloadManifest();
|
|
}
|
|
}
|
|
|
|
void AssetsManagerEx::parseVersion()
|
|
{
|
|
if (_updateState != State::VERSION_LOADED)
|
|
return;
|
|
|
|
_remoteManifest->parseVersion(_tempVersionPath);
|
|
|
|
if (!_remoteManifest->isVersionLoaded())
|
|
{
|
|
AXLOG("AssetsManagerEx : Fail to parse version file, step skipped\n");
|
|
_updateState = State::PREDOWNLOAD_MANIFEST;
|
|
downloadManifest();
|
|
}
|
|
else
|
|
{
|
|
if (_localManifest->versionGreater(_remoteManifest, _versionCompareHandle))
|
|
{
|
|
_updateState = State::UP_TO_DATE;
|
|
_fileUtils->removeDirectory(_tempStoragePath);
|
|
dispatchUpdateEvent(EventAssetsManagerEx::EventCode::ALREADY_UP_TO_DATE);
|
|
}
|
|
else
|
|
{
|
|
_updateState = State::NEED_UPDATE;
|
|
|
|
// Wait to update so continue the process
|
|
if (_updateEntry == UpdateEntry::DO_UPDATE)
|
|
{
|
|
// dispatch after checking update entry because event dispatching may modify the update entry
|
|
dispatchUpdateEvent(EventAssetsManagerEx::EventCode::NEW_VERSION_FOUND);
|
|
_updateState = State::PREDOWNLOAD_MANIFEST;
|
|
downloadManifest();
|
|
}
|
|
else
|
|
{
|
|
dispatchUpdateEvent(EventAssetsManagerEx::EventCode::NEW_VERSION_FOUND);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void AssetsManagerEx::downloadManifest()
|
|
{
|
|
if (_updateState != State::PREDOWNLOAD_MANIFEST)
|
|
return;
|
|
|
|
std::string manifestUrl;
|
|
if (_remoteManifest->isVersionLoaded())
|
|
{
|
|
manifestUrl = _remoteManifest->getManifestFileUrl();
|
|
}
|
|
else
|
|
{
|
|
manifestUrl = _localManifest->getManifestFileUrl();
|
|
}
|
|
|
|
if (!manifestUrl.empty())
|
|
{
|
|
_updateState = State::DOWNLOADING_MANIFEST;
|
|
// Download version file asynchronously
|
|
_downloader->createDownloadFileTask(manifestUrl, _tempManifestPath, MANIFEST_ID);
|
|
}
|
|
// No manifest file found
|
|
else
|
|
{
|
|
AXLOG("AssetsManagerEx : No manifest file found, check update failed\n");
|
|
dispatchUpdateEvent(EventAssetsManagerEx::EventCode::ERROR_DOWNLOAD_MANIFEST);
|
|
_updateState = State::UNCHECKED;
|
|
}
|
|
}
|
|
|
|
void AssetsManagerEx::parseManifest()
|
|
{
|
|
if (_updateState != State::MANIFEST_LOADED)
|
|
return;
|
|
|
|
_remoteManifest->parse(_tempManifestPath);
|
|
|
|
if (!_remoteManifest->isLoaded())
|
|
{
|
|
AXLOG("AssetsManagerEx : Error parsing manifest file, %s", _tempManifestPath.c_str());
|
|
dispatchUpdateEvent(EventAssetsManagerEx::EventCode::ERROR_PARSE_MANIFEST);
|
|
_updateState = State::UNCHECKED;
|
|
}
|
|
else
|
|
{
|
|
if (_localManifest->versionGreater(_remoteManifest, _versionCompareHandle))
|
|
{
|
|
_updateState = State::UP_TO_DATE;
|
|
_fileUtils->removeDirectory(_tempStoragePath);
|
|
dispatchUpdateEvent(EventAssetsManagerEx::EventCode::ALREADY_UP_TO_DATE);
|
|
}
|
|
else
|
|
{
|
|
_updateState = State::NEED_UPDATE;
|
|
dispatchUpdateEvent(EventAssetsManagerEx::EventCode::NEW_VERSION_FOUND);
|
|
|
|
if (_updateEntry == UpdateEntry::DO_UPDATE)
|
|
{
|
|
startUpdate();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void AssetsManagerEx::startUpdate()
|
|
{
|
|
if (_updateState != State::NEED_UPDATE)
|
|
return;
|
|
|
|
_updateState = State::UPDATING;
|
|
// Clean up before update
|
|
_failedUnits.clear();
|
|
_downloadUnits.clear();
|
|
_totalWaitToDownload = _totalToDownload = 0;
|
|
_nextSavePoint = 0;
|
|
_percent = _percentByFile = _sizeCollected = _totalSize = 0;
|
|
_downloadedSize.clear();
|
|
_totalEnabled = false;
|
|
|
|
// Temporary manifest exists, resuming previous download
|
|
if (_tempManifest && _tempManifest->isLoaded() && _tempManifest->versionEquals(_remoteManifest))
|
|
{
|
|
_tempManifest->saveToFile(_tempManifestPath);
|
|
_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);
|
|
dispatchUpdateEvent(EventAssetsManagerEx::EventCode::UPDATE_PROGRESSION, "", msg);
|
|
}
|
|
else
|
|
{
|
|
// Temporary manifest exists, but can't be parsed or version doesn't equals remote manifest (out of date)
|
|
if (_tempManifest)
|
|
{
|
|
// Remove all temp files
|
|
_fileUtils->removeDirectory(_tempStoragePath);
|
|
AX_SAFE_RELEASE(_tempManifest);
|
|
// Recreate temp storage path and save remote manifest
|
|
_fileUtils->createDirectory(_tempStoragePath);
|
|
_remoteManifest->saveToFile(_tempManifestPath);
|
|
}
|
|
|
|
// Temporary manifest will be used to register the download states of each asset,
|
|
// in this case, it equals remote manifest.
|
|
_tempManifest = _remoteManifest;
|
|
|
|
// Check difference between local manifest and remote manifest
|
|
hlookup::string_map<Manifest::AssetDiff> diff_map = _localManifest->genDiff(_remoteManifest);
|
|
if (diff_map.empty())
|
|
{
|
|
updateSucceed();
|
|
}
|
|
else
|
|
{
|
|
// Generate download units for all assets that need to be updated or added
|
|
std::string_view packageUrl = _remoteManifest->getPackageUrl();
|
|
// Save current download manifest information for resuming
|
|
_tempManifest->saveToFile(_tempManifestPath);
|
|
// Preprocessing local files in previous version and creating download folders
|
|
for (auto it = diff_map.begin(); it != diff_map.end(); ++it)
|
|
{
|
|
Manifest::AssetDiff diff = it->second;
|
|
if (diff.type != Manifest::DiffType::DELETED)
|
|
{
|
|
auto& path = diff.asset.path;
|
|
DownloadUnit unit;
|
|
unit.customId = it->first;
|
|
unit.srcUrl = packageUrl;
|
|
unit.srcUrl += path;
|
|
unit.storagePath = _tempStoragePath + path;
|
|
unit.size = diff.asset.size;
|
|
_downloadUnits.emplace(unit.customId, unit);
|
|
_tempManifest->setAssetDownloadState(it->first, Manifest::DownloadState::UNSTARTED);
|
|
}
|
|
}
|
|
_totalWaitToDownload = _totalToDownload = (int)_downloadUnits.size();
|
|
this->batchDownload();
|
|
|
|
std::string msg = StringUtils::format("Start to update %d files from remote package.", _totalToDownload);
|
|
dispatchUpdateEvent(EventAssetsManagerEx::EventCode::UPDATE_PROGRESSION, "", msg);
|
|
}
|
|
}
|
|
}
|
|
|
|
void AssetsManagerEx::updateSucceed()
|
|
{
|
|
// Every thing is correctly downloaded, do the following
|
|
// 1. rename temporary manifest to valid manifest
|
|
std::string tempFileName = TEMP_MANIFEST_FILENAME;
|
|
std::string fileName = MANIFEST_FILENAME;
|
|
_fileUtils->renameFile(_tempStoragePath, tempFileName, fileName);
|
|
// 2. merge temporary storage path to storage path so that temporary version turns to cached version
|
|
if (_fileUtils->isDirectoryExist(_tempStoragePath))
|
|
{
|
|
// Merging all files in temp storage path to storage path
|
|
std::vector<std::string> files;
|
|
_fileUtils->listFilesRecursively(_tempStoragePath, &files);
|
|
int baseOffset = (int)_tempStoragePath.length();
|
|
std::string relativePath, dstPath;
|
|
for (std::vector<std::string>::iterator it = files.begin(); it != files.end(); ++it)
|
|
{
|
|
relativePath.assign((*it).substr(baseOffset));
|
|
dstPath.assign(_storagePath + relativePath);
|
|
// Create directory
|
|
if (relativePath.back() == '/')
|
|
{
|
|
_fileUtils->createDirectory(dstPath);
|
|
}
|
|
// Copy file
|
|
else
|
|
{
|
|
if (_fileUtils->isFileExist(dstPath))
|
|
{
|
|
_fileUtils->removeFile(dstPath);
|
|
}
|
|
_fileUtils->renameFile(*it, dstPath);
|
|
}
|
|
}
|
|
// Remove temp storage path
|
|
_fileUtils->removeDirectory(_tempStoragePath);
|
|
}
|
|
// 3. swap the localManifest
|
|
AX_SAFE_RELEASE(_localManifest);
|
|
_localManifest = _remoteManifest;
|
|
_localManifest->setManifestRoot(_storagePath);
|
|
_remoteManifest = nullptr;
|
|
// 4. make local manifest take effect
|
|
prepareLocalManifest();
|
|
// 5. Set update state
|
|
_updateState = State::UP_TO_DATE;
|
|
// 6. Notify finished event
|
|
dispatchUpdateEvent(EventAssetsManagerEx::EventCode::UPDATE_FINISHED);
|
|
}
|
|
|
|
void AssetsManagerEx::checkUpdate()
|
|
{
|
|
if (_updateEntry != UpdateEntry::NONE)
|
|
{
|
|
AXLOGERROR("AssetsManagerEx::checkUpdate, updateEntry isn't NONE");
|
|
return;
|
|
}
|
|
|
|
if (!_inited)
|
|
{
|
|
AXLOG("AssetsManagerEx : Manifests uninited.\n");
|
|
dispatchUpdateEvent(EventAssetsManagerEx::EventCode::ERROR_NO_LOCAL_MANIFEST);
|
|
return;
|
|
}
|
|
if (!_localManifest->isLoaded())
|
|
{
|
|
AXLOG("AssetsManagerEx : No local manifest file found error.\n");
|
|
dispatchUpdateEvent(EventAssetsManagerEx::EventCode::ERROR_NO_LOCAL_MANIFEST);
|
|
return;
|
|
}
|
|
|
|
_updateEntry = UpdateEntry::CHECK_UPDATE;
|
|
|
|
switch (_updateState)
|
|
{
|
|
case State::UNCHECKED:
|
|
case State::PREDOWNLOAD_VERSION:
|
|
{
|
|
downloadVersion();
|
|
}
|
|
break;
|
|
case State::UP_TO_DATE:
|
|
{
|
|
dispatchUpdateEvent(EventAssetsManagerEx::EventCode::ALREADY_UP_TO_DATE);
|
|
}
|
|
break;
|
|
case State::FAIL_TO_UPDATE:
|
|
case State::NEED_UPDATE:
|
|
{
|
|
dispatchUpdateEvent(EventAssetsManagerEx::EventCode::NEW_VERSION_FOUND);
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void AssetsManagerEx::update()
|
|
{
|
|
if (_updateEntry != UpdateEntry::NONE)
|
|
{
|
|
AXLOGERROR("AssetsManagerEx::update, updateEntry isn't NONE");
|
|
return;
|
|
}
|
|
|
|
if (!_inited)
|
|
{
|
|
AXLOG("AssetsManagerEx : Manifests uninited.\n");
|
|
dispatchUpdateEvent(EventAssetsManagerEx::EventCode::ERROR_NO_LOCAL_MANIFEST);
|
|
return;
|
|
}
|
|
if (!_localManifest->isLoaded())
|
|
{
|
|
AXLOG("AssetsManagerEx : No local manifest file found error.\n");
|
|
dispatchUpdateEvent(EventAssetsManagerEx::EventCode::ERROR_NO_LOCAL_MANIFEST);
|
|
return;
|
|
}
|
|
|
|
_updateEntry = UpdateEntry::DO_UPDATE;
|
|
|
|
switch (_updateState)
|
|
{
|
|
case State::UNCHECKED:
|
|
{
|
|
_updateState = State::PREDOWNLOAD_VERSION;
|
|
}
|
|
case State::PREDOWNLOAD_VERSION:
|
|
{
|
|
downloadVersion();
|
|
}
|
|
break;
|
|
case State::VERSION_LOADED:
|
|
{
|
|
parseVersion();
|
|
}
|
|
break;
|
|
case State::PREDOWNLOAD_MANIFEST:
|
|
{
|
|
downloadManifest();
|
|
}
|
|
break;
|
|
case State::MANIFEST_LOADED:
|
|
{
|
|
parseManifest();
|
|
}
|
|
break;
|
|
case State::FAIL_TO_UPDATE:
|
|
case State::NEED_UPDATE:
|
|
{
|
|
// Manifest not loaded yet
|
|
if (!_remoteManifest->isLoaded())
|
|
{
|
|
_updateState = State::PREDOWNLOAD_MANIFEST;
|
|
downloadManifest();
|
|
}
|
|
else
|
|
{
|
|
startUpdate();
|
|
}
|
|
}
|
|
break;
|
|
case State::UP_TO_DATE:
|
|
case State::UPDATING:
|
|
case State::UNZIPPING:
|
|
_updateEntry = UpdateEntry::NONE;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void AssetsManagerEx::updateAssets(const DownloadUnits& assets)
|
|
{
|
|
if (!_inited)
|
|
{
|
|
AXLOG("AssetsManagerEx : Manifests uninited.\n");
|
|
dispatchUpdateEvent(EventAssetsManagerEx::EventCode::ERROR_NO_LOCAL_MANIFEST);
|
|
return;
|
|
}
|
|
|
|
if (_updateState != State::UPDATING && _localManifest->isLoaded() && _remoteManifest->isLoaded())
|
|
{
|
|
_updateState = State::UPDATING;
|
|
_downloadUnits.clear();
|
|
_downloadedSize.clear();
|
|
_percent = _percentByFile = _sizeCollected = _totalSize = 0;
|
|
_totalWaitToDownload = _totalToDownload = (int)assets.size();
|
|
_nextSavePoint = 0;
|
|
_totalEnabled = false;
|
|
if (_totalToDownload > 0)
|
|
{
|
|
_downloadUnits = assets;
|
|
this->batchDownload();
|
|
}
|
|
else if (_totalToDownload == 0)
|
|
{
|
|
onDownloadUnitsFinished();
|
|
}
|
|
}
|
|
}
|
|
|
|
const DownloadUnits& AssetsManagerEx::getFailedAssets() const
|
|
{
|
|
return _failedUnits;
|
|
}
|
|
|
|
void AssetsManagerEx::downloadFailedAssets()
|
|
{
|
|
AXLOG("AssetsManagerEx : Start update %lu failed assets.\n", static_cast<unsigned long>(_failedUnits.size()));
|
|
updateAssets(_failedUnits);
|
|
}
|
|
|
|
void AssetsManagerEx::fileError(std::string_view identifier,
|
|
std::string_view errorStr,
|
|
int errorCode,
|
|
int errorCodeInternal)
|
|
{
|
|
auto unitIt = _downloadUnits.find(identifier);
|
|
// Found unit and add it to failed units
|
|
if (unitIt != _downloadUnits.end())
|
|
{
|
|
_totalWaitToDownload--;
|
|
|
|
DownloadUnit unit = unitIt->second;
|
|
_failedUnits.emplace(unit.customId, unit);
|
|
}
|
|
dispatchUpdateEvent(EventAssetsManagerEx::EventCode::ERROR_UPDATING, identifier, errorStr, errorCode,
|
|
errorCodeInternal);
|
|
_tempManifest->setAssetDownloadState(identifier, Manifest::DownloadState::UNSTARTED);
|
|
|
|
_currConcurrentTask = MAX(0, _currConcurrentTask - 1);
|
|
queueDowload();
|
|
}
|
|
|
|
void AssetsManagerEx::fileSuccess(std::string_view customId, std::string_view storagePath)
|
|
{
|
|
// Set download state to SUCCESSED
|
|
_tempManifest->setAssetDownloadState(customId, Manifest::DownloadState::SUCCESSED);
|
|
|
|
auto unitIt = _failedUnits.find(customId);
|
|
// Found unit and delete it
|
|
if (unitIt != _failedUnits.end())
|
|
{
|
|
// Remove from failed units list
|
|
_failedUnits.erase(unitIt);
|
|
}
|
|
|
|
unitIt = _downloadUnits.find(customId);
|
|
if (unitIt != _downloadUnits.end())
|
|
{
|
|
// Reduce count only when unit found in _downloadUnits
|
|
_totalWaitToDownload--;
|
|
|
|
_percent = _percentByFile = 100 * (float)(_totalToDownload - _totalWaitToDownload) / _totalToDownload;
|
|
// Notify progression event
|
|
dispatchUpdateEvent(EventAssetsManagerEx::EventCode::UPDATE_PROGRESSION, "");
|
|
}
|
|
// Notify asset updated event
|
|
dispatchUpdateEvent(EventAssetsManagerEx::EventCode::ASSET_UPDATED, customId);
|
|
|
|
_currConcurrentTask = MAX(0, _currConcurrentTask - 1);
|
|
queueDowload();
|
|
}
|
|
|
|
void AssetsManagerEx::onError(const network::DownloadTask& task,
|
|
int errorCode,
|
|
int errorCodeInternal,
|
|
std::string_view errorStr)
|
|
{
|
|
// Skip version error occurred
|
|
if (task.identifier == VERSION_ID)
|
|
{
|
|
AXLOG("AssetsManagerEx : Fail to download version file, step skipped\n");
|
|
_updateState = State::PREDOWNLOAD_MANIFEST;
|
|
downloadManifest();
|
|
}
|
|
else if (task.identifier == MANIFEST_ID)
|
|
{
|
|
dispatchUpdateEvent(EventAssetsManagerEx::EventCode::ERROR_DOWNLOAD_MANIFEST, task.identifier, errorStr,
|
|
errorCode, errorCodeInternal);
|
|
_updateState = State::FAIL_TO_UPDATE;
|
|
}
|
|
else
|
|
{
|
|
fileError(task.identifier, errorStr, errorCode, errorCodeInternal);
|
|
}
|
|
}
|
|
|
|
void AssetsManagerEx::onProgress(double total, double downloaded, std::string_view /*url*/, std::string_view customId)
|
|
{
|
|
if (customId == VERSION_ID || customId == MANIFEST_ID)
|
|
{
|
|
_percent = 100 * downloaded / total;
|
|
// Notify progression event
|
|
dispatchUpdateEvent(EventAssetsManagerEx::EventCode::UPDATE_PROGRESSION, customId);
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
// Calcul total downloaded
|
|
bool found = false;
|
|
double totalDownloaded = 0;
|
|
for (auto it = _downloadedSize.begin(); it != _downloadedSize.end(); ++it)
|
|
{
|
|
if (it->first == customId)
|
|
{
|
|
it->second = downloaded;
|
|
found = true;
|
|
}
|
|
totalDownloaded += it->second;
|
|
}
|
|
// Collect information if not registed
|
|
if (!found)
|
|
{
|
|
// Set download state to DOWNLOADING, this will run only once in the download process
|
|
_tempManifest->setAssetDownloadState(customId, Manifest::DownloadState::DOWNLOADING);
|
|
// Register the download size information
|
|
_downloadedSize.emplace(customId, downloaded);
|
|
// Check download unit size existance, if not exist collect size in total size
|
|
if (_downloadUnits[customId].size == 0)
|
|
{
|
|
_totalSize += total;
|
|
_sizeCollected++;
|
|
// All collected, enable total size
|
|
if (_sizeCollected == _totalToDownload)
|
|
{
|
|
_totalEnabled = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (_totalEnabled && _updateState == State::UPDATING)
|
|
{
|
|
float currentPercent = 100 * totalDownloaded / _totalSize;
|
|
// Notify at integer level change
|
|
if ((int)currentPercent != (int)_percent)
|
|
{
|
|
_percent = currentPercent;
|
|
// Notify progression event
|
|
dispatchUpdateEvent(EventAssetsManagerEx::EventCode::UPDATE_PROGRESSION, customId);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void AssetsManagerEx::onSuccess(std::string_view /*srcUrl*/, std::string_view storagePath, std::string_view customId)
|
|
{
|
|
if (customId == VERSION_ID)
|
|
{
|
|
_updateState = State::VERSION_LOADED;
|
|
parseVersion();
|
|
}
|
|
else if (customId == MANIFEST_ID)
|
|
{
|
|
_updateState = State::MANIFEST_LOADED;
|
|
parseManifest();
|
|
}
|
|
else
|
|
{
|
|
bool ok = true;
|
|
auto& assets = _remoteManifest->getAssets();
|
|
auto assetIt = assets.find(customId);
|
|
if (assetIt != assets.end())
|
|
{
|
|
Manifest::Asset asset = assetIt->second;
|
|
if (_verifyCallback != nullptr)
|
|
{
|
|
ok = _verifyCallback(storagePath, asset);
|
|
}
|
|
}
|
|
|
|
if (ok)
|
|
{
|
|
bool compressed = assetIt != assets.end() ? assetIt->second.compressed : false;
|
|
if (compressed)
|
|
{
|
|
decompressDownloadedZip(customId, storagePath);
|
|
}
|
|
else
|
|
{
|
|
fileSuccess(customId, storagePath);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
fileError(customId, "Asset file verification failed after downloaded");
|
|
}
|
|
}
|
|
}
|
|
|
|
void AssetsManagerEx::destroyDownloadedVersion()
|
|
{
|
|
_fileUtils->removeDirectory(_storagePath);
|
|
_fileUtils->removeDirectory(_tempStoragePath);
|
|
}
|
|
|
|
void AssetsManagerEx::batchDownload()
|
|
{
|
|
_queue.clear();
|
|
for (const auto& iter : _downloadUnits)
|
|
{
|
|
const DownloadUnit& unit = iter.second;
|
|
if (unit.size > 0)
|
|
{
|
|
_totalSize += unit.size;
|
|
_sizeCollected++;
|
|
}
|
|
|
|
_queue.push_back(iter.first);
|
|
}
|
|
// All collected, enable total size
|
|
if (_sizeCollected == _totalToDownload)
|
|
{
|
|
_totalEnabled = true;
|
|
}
|
|
|
|
queueDowload();
|
|
}
|
|
|
|
void AssetsManagerEx::queueDowload()
|
|
{
|
|
if (_totalWaitToDownload == 0)
|
|
{
|
|
this->onDownloadUnitsFinished();
|
|
return;
|
|
}
|
|
|
|
while (_currConcurrentTask < _maxConcurrentTask && !_queue.empty())
|
|
{
|
|
std::string key = _queue.back();
|
|
_queue.pop_back();
|
|
|
|
_currConcurrentTask++;
|
|
DownloadUnit& unit = _downloadUnits[key];
|
|
_fileUtils->createDirectory(basename(unit.storagePath));
|
|
_downloader->createDownloadFileTask(unit.srcUrl, unit.storagePath, unit.customId);
|
|
|
|
_tempManifest->setAssetDownloadState(key, Manifest::DownloadState::DOWNLOADING);
|
|
}
|
|
if (_percentByFile / 100 > _nextSavePoint)
|
|
{
|
|
// Save current download manifest information for resuming
|
|
_tempManifest->saveToFile(_tempManifestPath);
|
|
_nextSavePoint += SAVE_POINT_INTERVAL;
|
|
}
|
|
}
|
|
|
|
void AssetsManagerEx::onDownloadUnitsFinished()
|
|
{
|
|
// Finished with error check
|
|
if (!_failedUnits.empty())
|
|
{
|
|
// Save current download manifest information for resuming
|
|
_tempManifest->saveToFile(_tempManifestPath);
|
|
|
|
_updateState = State::FAIL_TO_UPDATE;
|
|
dispatchUpdateEvent(EventAssetsManagerEx::EventCode::UPDATE_FAILED);
|
|
}
|
|
else if (_updateState == State::UPDATING)
|
|
{
|
|
updateSucceed();
|
|
}
|
|
}
|
|
|
|
void AssetsManagerEx::fillZipFunctionOverrides(zlib_filefunc_def_s& zipFunctionOverrides)
|
|
{
|
|
zipFunctionOverrides.zopen_file = AssetManagerEx_open_file_func;
|
|
zipFunctionOverrides.zopendisk_file = AssetManagerEx_opendisk_file_func;
|
|
zipFunctionOverrides.zread_file = AssetManagerEx_read_file_func;
|
|
zipFunctionOverrides.zwrite_file = AssetManagerEx_write_file_func;
|
|
zipFunctionOverrides.ztell_file = AssetManagerEx_tell_file_func;
|
|
zipFunctionOverrides.zseek_file = AssetManagerEx_seek_file_func;
|
|
zipFunctionOverrides.zclose_file = AssetManagerEx_close_file_func;
|
|
zipFunctionOverrides.zerror_file = AssetManagerEx_error_file_func;
|
|
zipFunctionOverrides.opaque = nullptr;
|
|
}
|
|
|
|
NS_AX_EXT_END
|