Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixed the issue of the main page getting stuck due to the long execution time required for updates #17626

Merged
merged 5 commits into from
Sep 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
185 changes: 108 additions & 77 deletions native/extensions/assets-manager/AssetsManagerEx.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ NS_CC_EXT_BEGIN
const std::string AssetsManagerEx::VERSION_ID = "@version";
const std::string AssetsManagerEx::MANIFEST_ID = "@manifest";

AssetsManagerEx* AssetsManagerEx::assetsManager = nullptr;

// Implementation of AssetsManagerEx

AssetsManagerEx::AssetsManagerEx(const std::string &manifestUrl, const std::string &storagePath) {
Expand All @@ -69,6 +71,8 @@ AssetsManagerEx::AssetsManagerEx(const std::string &manifestUrl, const std::stri
}

void AssetsManagerEx::init(const std::string &manifestUrl, const std::string &storagePath) {
assetsManager = this;

// Init variables
std::string pointer = StringUtils::format("%p", this);
_eventName = "__cc_assets_manager_" + pointer;
Expand Down Expand Up @@ -110,6 +114,7 @@ AssetsManagerEx::~AssetsManagerEx() {
CC_SAFE_RELEASE(_tempManifest);
}
CC_SAFE_RELEASE(_remoteManifest);
assetsManager = nullptr;
}

AssetsManagerEx *AssetsManagerEx::create(const std::string &manifestUrl, const std::string &storagePath) {
Expand Down Expand Up @@ -421,8 +426,8 @@ bool AssetsManagerEx::decompress(const std::string &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
// 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
CC_LOG_DEBUG("AssetsManagerEx : can not create directory %s\n", fullPath.c_str());
Expand Down Expand Up @@ -552,7 +557,8 @@ void AssetsManagerEx::dispatchUpdateEvent(EventAssetsManagerEx::EventCode code,
break;
}

if (_eventCallback != nullptr) {
// If more than one instance is spawned, then the event callback will fail, so a judgment call needs to be made.
if (_eventCallback != nullptr && assetsManager == this) {
auto *event = ccnew EventAssetsManagerEx(_eventName, this, code, assetId, message, curleCode, curlmCode);
event->addRef();
_eventCallback(event);
Expand Down Expand Up @@ -648,20 +654,28 @@ void AssetsManagerEx::parseManifest() {

if (_updateEntry == UpdateEntry::DO_UPDATE) {
startUpdate();
dispatchUpdateEvent(EventAssetsManagerEx::EventCode::NEW_VERSION_FOUND);
} else if (_updateEntry == UpdateEntry::CHECK_UPDATE) {
prepareUpdate();
auto cb = [this]() {
dispatchUpdateEvent(EventAssetsManagerEx::EventCode::NEW_VERSION_FOUND);
};
prepareUpdateAsync(cb);
}

dispatchUpdateEvent(EventAssetsManagerEx::EventCode::NEW_VERSION_FOUND);
}
}
}

void AssetsManagerEx::prepareUpdate() {
if (_updateState != State::NEED_UPDATE) {
prepareUpdateAsync();
}

void AssetsManagerEx::prepareUpdateAsync(const PrepareUpdateFinishedCallback &cb) {
// Avoiding multiple function calls.
if (_updateState != State::NEED_UPDATE || _updateState == State::PREPARE_UPDATING) {
CC_LOG_WARNING("The current state does not need to be updated or is being executed.");
return;
}

_updateState = State::PREPARE_UPDATING;
// Clean up before update
_failedUnits.clear();
_downloadUnits.clear();
Expand All @@ -673,83 +687,100 @@ void AssetsManagerEx::prepareUpdate() {
_downloadResumed = false;
_downloadedSize.clear();
_totalEnabled = false;

// Temporary manifest exists, previously updating and equals to the remote version, resuming previous download
if (_tempManifest && _tempManifest->isLoaded() && _tempManifest->isUpdating() && _tempManifest->versionEquals(_remoteManifest)) {
_tempManifest->saveToFile(_tempManifestPath);
_tempManifest->genResumeAssetsList(&_downloadUnits);
_totalWaitToDownload = _totalToDownload = static_cast<int>(_downloadUnits.size());
_downloadResumed = true;

// Collect total size
for (const auto &iter : _downloadUnits) {
const DownloadUnit &unit = iter.second;
if (unit.size > 0) {
_totalSize += unit.size;
}
}
} 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);
CC_SAFE_RELEASE(_tempManifest);
// Recreate temp storage path and save remote manifest
_fileUtils->createDirectory(_tempStoragePath);
_remoteManifest->saveToFile(_tempManifestPath);
std::function<void(void *)> prepareFinished = [this, cb](void * param) {
CC_UNUSED_PARAM(param);
_updateState = State::READY_TO_UPDATE;
if (cb) {
cb();
}
this->release();
};
this->addRef();
// If there are many diffMap, it will lead to a very time-consuming run. So here it needs to be executed in a thread.
AsyncTaskPool::getInstance()->enqueue(AsyncTaskPool::TaskType::TASK_OTHER, prepareFinished, nullptr, [this]() {
// Temporary manifest exists, previously updating and equals to the remote version, resuming previous download
if (_tempManifest && _tempManifest->isLoaded() && _tempManifest->isUpdating() && _tempManifest->versionEquals(_remoteManifest)) {
_tempManifest->saveToFile(_tempManifestPath);
_tempManifest->genResumeAssetsList(&_downloadUnits);
_totalWaitToDownload = _totalToDownload = static_cast<int>(_downloadUnits.size());
_downloadResumed = true;

// Collect total size
for (const auto &iter : _downloadUnits) {
const DownloadUnit &unit = iter.second;
if (unit.size > 0) {
_totalSize += unit.size;
}
}
} 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);
CC_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
std::unordered_map<std::string, Manifest::AssetDiff> diffMap = _localManifest->genDiff(_remoteManifest);
if (diffMap.empty()) {
updateSucceed();
return;
} // Generate download units for all assets that need to be updated or added
std::string packageUrl = _remoteManifest->getPackageUrl();
// Preprocessing local files in previous version and creating download folders
for (auto &it : diffMap) {
Manifest::AssetDiff diff = it.second;
if (diff.type != Manifest::DiffType::DELETED) {
std::string path = diff.asset.path;
DownloadUnit unit;
unit.customId = it.first;
unit.srcUrl = packageUrl + path + "?md5=" + diff.asset.md5;
unit.storagePath = _tempStoragePath + path;
unit.size = diff.asset.size;
_downloadUnits.emplace(unit.customId, unit);
_tempManifest->setAssetDownloadState(it.first, Manifest::DownloadState::UNSTARTED);
_totalSize += unit.size;
// 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
std::unordered_map<std::string, Manifest::AssetDiff> diffMap = _localManifest->genDiff(_remoteManifest);
if (diffMap.empty()) {
updateSucceed();
return;
} // Generate download units for all assets that need to be updated or added
std::string packageUrl = _remoteManifest->getPackageUrl();
// Preprocessing local files in previous version and creating download folders
auto prevTime = std::chrono::steady_clock::now();
DownloadUnit unit;
for (auto &it : diffMap) {
Manifest::AssetDiff diff = it.second;
if (diff.type != Manifest::DiffType::DELETED) {
const std::string &path = diff.asset.path;
unit.customId = it.first;
unit.srcUrl = packageUrl + path + "?md5=" + diff.asset.md5;
unit.storagePath = _tempStoragePath + path;
unit.size = diff.asset.size;
_downloadUnits.emplace(unit.customId, unit);
_tempManifest->setAssetDownloadState(it.first, Manifest::DownloadState::UNSTARTED);
_totalSize += unit.size;
}
}
}
// Start updating the temp manifest
_tempManifest->setUpdating(true);
// Save current download manifest information for resuming
_tempManifest->saveToFile(_tempManifestPath);
// Start updating the temp manifest
_tempManifest->setUpdating(true);
// Save current download manifest information for resuming
_tempManifest->saveToFile(_tempManifestPath);

_totalWaitToDownload = _totalToDownload = static_cast<int>(_downloadUnits.size());
}
_updateState = State::READY_TO_UPDATE;
_totalWaitToDownload = _totalToDownload = static_cast<int>(_downloadUnits.size());
}
});
}

void AssetsManagerEx::startUpdate() {
if (_updateState == State::NEED_UPDATE) {
prepareUpdate();
}
if (_updateState == State::READY_TO_UPDATE) {
_totalSize = 0;
_updateState = State::UPDATING;
std::string msg;
if (_downloadResumed) {
msg = StringUtils::format("Resuming from previous unfinished update, %d files remains to be finished.", _totalToDownload);
} else {
msg = StringUtils::format("Start to update %d files from remote package.", _totalToDownload);
auto cb = [this]() {
if (_updateState == State::READY_TO_UPDATE) {
_totalSize = 0;
_updateState = State::UPDATING;
std::string msg;
if (_downloadResumed) {
msg = StringUtils::format("Resuming from previous unfinished update, %d files remains to be finished.", _totalToDownload);
} else {
msg = StringUtils::format("Start to update %d files from remote package.", _totalToDownload);
}
if (this == assetsManager) {
dispatchUpdateEvent(EventAssetsManagerEx::EventCode::UPDATE_PROGRESSION, "", msg);
batchDownload();
}
}
dispatchUpdateEvent(EventAssetsManagerEx::EventCode::UPDATE_PROGRESSION, "", msg);
batchDownload();
};
if (_updateState == State::NEED_UPDATE) {
prepareUpdateAsync(cb);
} else {
cb();
}
}

Expand Down
10 changes: 10 additions & 0 deletions native/extensions/assets-manager/AssetsManagerEx.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ class CC_EX_DLL AssetsManagerEx : public RefCounted {
DOWNLOADING_MANIFEST,
MANIFEST_LOADED,
NEED_UPDATE,
PREPARE_UPDATING,
READY_TO_UPDATE,
UPDATING,
UNZIPPING,
Expand Down Expand Up @@ -292,6 +293,14 @@ class CC_EX_DLL AssetsManagerEx : public RefCounted {
*/
virtual void onSuccess(const std::string &srcUrl, const std::string &storagePath, const std::string &customId);

/** @brief prepareUpdate may take a long time to execute, so we need to do it asynchronously.
@param cb Function that are called at the end of the prepareUpdate execution and are running callbacks in the main thread.
* @js NA
* @lua NA
*/
using PrepareUpdateFinishedCallback = std::function<void()>;
void prepareUpdateAsync(const PrepareUpdateFinishedCallback &cb = PrepareUpdateFinishedCallback());

private:
void batchDownload();

Expand Down Expand Up @@ -405,6 +414,7 @@ class CC_EX_DLL AssetsManagerEx : public RefCounted {
//! Callback function to dispatch events
EventCallback _eventCallback = nullptr;

static AssetsManagerEx *assetsManager;
//! Marker for whether the assets manager is inited
bool _inited = false;
};
Expand Down
Loading