2022-08-26 16:48:17 +08:00

426 lines
19 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import BusinessTypeSetting from "../../_BusinessTypeSetting/BusinessTypeSetting";
import Enum_Loading from "../HUDV2/Enum_Loading";
import AssetBundleMamager from "./AssetBundleMamager";
const { ccclass, property } = cc._decorator;
/** HUDManager */
@ccclass
export default class HUDM extends cc.Component {
//#region static 屬性
private static _instance: HUDM = null;
public static get Instance(): HUDM { return HUDM._instance; }
//#endregion
//#region static 屬性
public BundleName: string = "";
//#endregion
//#region private 屬性
private _am: jsb.AssetsManager;
private _onFileProgress: (finish: number, total: number, item: string) => void;
private _updateListener: any;
private _checkListener: any;
private _versionCompareHandle: any = null;
private _needUpdateData: Enum_Loading.NeedUpdateDataObj = null;
private _updateingData: Enum_Loading.UpdateingDataObj = null;
private _updating: boolean = false;
private _canRetry: boolean = false;
private _isChangeUrl: boolean = false;
private _path: string = "Bundle";
private _customManifest: string = "";
private _storagePath: string = "";
//#endregion
//#region Lifecycle
constructor(...params: any[]) {
super();
if (!cc.sys.isNative) {
return;
} else if (params.length === 0) {
return;
}
HUDM._instance = this;
this.BundleName = params[0];
// let packageUrl: string = params[1];
// let BundleData: Enum_Loading.BundleDataObj = AssetBundleMamager.Instance.RemoteVerList[this.BundleName];
// let packageUrl: string = BundleData.BundleUrl;
let packageUrl: string = `${BusinessTypeSetting.UsePatch}${BusinessTypeSetting.FolderUrlBundle}${this.BundleName}`;
this._customManifest = JSON.stringify({
"packageUrl": packageUrl,
"remoteManifestUrl": `${packageUrl}/project.manifest`,
"remoteVersionUrl": `${packageUrl}/version.json`,
"version": "0.0.0",
});
this._storagePath = `${(jsb.fileUtils ? jsb.fileUtils.getWritablePath() : "./")}${this._path}/${this.BundleName}`;
// 本地熱更目錄下已存在project.manifest則直接修改已存在的project.manifest
if (AssetBundleMamager.Instance.IsChangeBundleUrl) {
if (jsb.fileUtils.isFileExist(this._storagePath + "/project.manifest")) {
this._isChangeUrl = true;
this._modifyAppLoadUrlForManifestFile(this._storagePath, packageUrl);
}
}
this._versionCompareHandle = function (versionA: string, versionB: string): number {
// console.log("Ver A " + versionA + "VerB " + versionB);
var vA: string[] = versionA.split(".");
var vB: string[] = versionB.split(".");
// 長度不相等,則進行更新
if (vA.length !== vB.length) {
return -1;
}
for (var i: number = 0; i < vA.length; ++i) {
var a: number = +vA[i];
var b: number = +vB[i] || 0;
if (a === b) {
// 數字相同,則跳過
continue;
} else {
// 數字不同,則進行更新
return -1;
}
}
// 長度相等且數字相等,則不更新
return 0;
};
this._initAssetManaget();
}
private _initAssetManaget(): void {
//
this._am = new jsb.AssetsManager("", this._storagePath, this._versionCompareHandle);
// Setup the verification callback, but we don't have md5 check function yet, so only print some message
// Return true if the verification passed, otherwise return false
this._am.setVerifyCallback(function (path: any, asset: { compressed: any; md5: any; path: any; size: any; }): boolean {
// When asset is compressed, we don't need to check its md5, because zip file have been deleted.
var compressed: any = asset.compressed;
// Retrieve the correct md5 value.
var expectedMD5: string = asset.md5;
// asset.path is relative path and path is absolute.
var relativePath: string = asset.path;
// The size of asset file, but this value could be absent.
var size: any = asset.size;
if (compressed) {
// panel.info.string = "Verification passed : " + relativePath;
// cc.log("onLoad -> Verification passed : " + relativePath);
return true;
} else {
// panel.info.string = "Verification passed : " + relativePath + ' (' + expectedMD5 + ')';
// cc.log("onLoad -> setVerifyCallbackVerification passed : " + relativePath + " (" + expectedMD5 + ")");
return true;
}
});
if (cc.sys.os === cc.sys.OS_ANDROID) {
// Some Android device may slow down the download process when concurrent tasks is too much.
// The value may not be accurate, please do more test and find what's most suitable for your game.
// this._am.setMaxConcurrentTask(10);
this._am["setMaxConcurrentTask"](10);
// this.panel.info.string = "Max concurrent tasks count have been limited to 2";
// cc.log("onLoad -> Max concurrent tasks count have been limited to 10");
}
}
private _modifyAppLoadUrlForManifestFile(filePath: string, newBundleUrl: string): void {
let allpath: string[] = [filePath, filePath + "_temp"];
let manifestname: string[] = ["project.manifest", "project.manifest.temp"];
for (var i: number = 0; i < allpath.length; ++i) {
let path: string = `${allpath[i]}/${manifestname[i]}`;
if (jsb.fileUtils.isFileExist(path)) {
// console.log(`[HUD] modifyAppLoadUrlForManifestFile: 有下載的manifest文件直接修改熱更地址`);
// 修改project.manifest
let projectManifest: string = jsb.fileUtils.getStringFromFile(path);
let projectManifestObj: any = JSON.parse(projectManifest);
projectManifestObj.packageUrl = newBundleUrl;
projectManifestObj.remoteManifestUrl = newBundleUrl + "/project.manifest";
projectManifestObj.remoteVersionUrl = newBundleUrl + "/version.json";
let afterString: string = JSON.stringify(projectManifestObj);
let isWrittenProject: boolean = jsb.fileUtils.writeStringToFile(afterString, path);
// // 更新數據庫中的新請求地址,下次如果檢測到不一致就重新修改 manifest 文件
// if (isWrittenProject) {
// LocalStorageData.Instance.BundleUrl = BusinessTypeSetting.UsePatch;
// }
// console.log("[HUD] 修改是否成功,project.manifest:", isWrittenProject);
// console.log("[HUD] 修改後文件:", projectManifestObj.packageUrl, projectManifestObj.remoteManifestUrl, projectManifestObj.remoteVersionUrl);
}
}
}
//#endregion
public *CheckUpdate(): IterableIterator<any> {
this._needUpdateData = null;
if (this._updating) {
// this.panel.info.string = 'Checking or updating ...';
console.error("checkUpdate -> Checking or updating ...");
return;
}
if (this._am.getState() === jsb.AssetsManager.State.UNINITED) {
let manifest: jsb.Manifest = new jsb.Manifest(this._customManifest, this._storagePath);
this._am.loadLocalManifest(manifest, this._storagePath);
}
if (!this._am.getLocalManifest() || !this._am.getLocalManifest().isLoaded()) {
// this.tipsLabel.string = "Failed to load local manifest ...";
console.error("checkUpdate -> Failed to load local manifest ...");
return;
}
this._am.setEventCallback(this.checkCb.bind(this));
this._am.checkUpdate();
this._updating = true;
while (this._needUpdateData === null) {
yield null;
}
let newBundleUrl: string = `${BusinessTypeSetting.UsePatch}${BusinessTypeSetting.FolderUrlBundle}${this.BundleName}`;
this._modifyAppLoadUrlForManifestFile(this._storagePath, newBundleUrl);
this._initAssetManaget();
let manifest: jsb.Manifest = new jsb.Manifest(this._customManifest, this._storagePath);
this._am.loadLocalManifest(manifest, this._storagePath);
if (!this._am.getLocalManifest() || !this._am.getLocalManifest().isLoaded()) {
// this.tipsLabel.string = "Failed to load local manifest ...";
console.error("checkUpdate -> Failed to load local manifest ...");
return;
}
// 更新動態路徑後再跑一次
this._am.setEventCallback(this.checkCb.bind(this));
this._needUpdateData = null;
this._am.checkUpdate();
this._updating = true;
while (this._needUpdateData === null) {
yield null;
}
if (this._isChangeUrl && (!this._needUpdateData.IsNeedUpdate || this._needUpdateData.TotalBytes === "0 B")) {
if (jsb.fileUtils.isFileExist(this._storagePath)) {
let isremoveDirectory: boolean = jsb.fileUtils.removeDirectory(this._storagePath);
let isremoveDirectory_temp: boolean = jsb.fileUtils.removeDirectory(this._storagePath + "_temp");
if (isremoveDirectory_temp) {
console.log(`removeDirectory: ${this._storagePath}_temp`);
}
if (isremoveDirectory) {
console.log(`removeDirectory: ${this._storagePath}`);
this._needUpdateData = null;
this._initAssetManaget();
this._needUpdateData = yield* this.CheckUpdate();
}
}
}
return this._needUpdateData;
}
private checkCb(event: jsb.EventAssetsManager): void {
var failed: boolean = false;
switch (event.getEventCode()) {
case jsb.EventAssetsManager.ERROR_NO_LOCAL_MANIFEST:
// this.tipsLabel.string = "No local manifest file found, HUD skipped.";
console.error("checkCb -> No local manifest file found, HUD skipped.");
failed = true;
break;
case jsb.EventAssetsManager.ERROR_DOWNLOAD_MANIFEST:
case jsb.EventAssetsManager.ERROR_PARSE_MANIFEST:
// this.tipsLabel.string = "Fail to download manifest file, HUD skipped.";
console.error("checkCb -> Fail to download manifest file, HUD skipped.");
failed = true;
break;
case jsb.EventAssetsManager.ALREADY_UP_TO_DATE:
// this.tipsLabel.string = "Already up to date with the latest remote version.";
// cc.log("checkCb -> Already up to date with the latest remote version.");
this._needUpdateData = new Enum_Loading.NeedUpdateDataObj(false);
break;
case jsb.EventAssetsManager.NEW_VERSION_FOUND:
// this.downloadLabel.node.active = true;
// this.downloadLabel.string = "New version found, please try to update." + event.getTotalBytes();
// this.panel.checkBtn.active = false;
// this.panel.fileProgress.progress = 0;
// this.panel.byteProgress.progress = 0;
// cc.log("checkCb -> New version found, please try to update." + event.getTotalBytes());
this._needUpdateData = new Enum_Loading.NeedUpdateDataObj(true, this._bytesToSize(event.getTotalBytes()));
break;
default:
return;
}
this._am.setEventCallback(null);
this._checkListener = null;
this._updating = false;
if (failed) {
//
}
}
public *HUD(onFileProgress?: (finish: number, total: number, item: string) => void): IterableIterator<any> {
this._updateingData = null;
if (this._am && !this._updating) {
this._am.setEventCallback(this._updateCb.bind(this));
if (this._am.getState() === jsb.AssetsManager.State.UNINITED) {
let manifest: jsb.Manifest = new jsb.Manifest(this._customManifest, this._storagePath);
this._am.loadLocalManifest(manifest, this._storagePath);
}
this._onFileProgress = onFileProgress ? onFileProgress : null;
this._am.update();
this._updating = true;
while (this._updateingData === null) {
yield null;
}
return this._updateingData;
} else {
return new Enum_Loading.UpdateingDataObj(false);
}
}
private _updateCb(event: jsb.EventAssetsManager): void {
var needRestart: boolean = false;
var failed: boolean = false;
switch (event.getEventCode()) {
case jsb.EventAssetsManager.ERROR_NO_LOCAL_MANIFEST:
// this.panel.info.string = 'No local manifest file found, HUD skipped.';
cc.log("updateCb -> No local manifest file found, HUD skipped.");
failed = true;
break;
case jsb.EventAssetsManager.UPDATE_PROGRESSION:
// this.panel.byteProgress.progress = event.getPercent();
// this.panel.fileProgress.progress = event.getPercentByFile();
// this.panel.fileLabel.string = event.getDownloadedFiles() + ' / ' + event.getTotalFiles();
// this.tipsLabel.string = event.getDownloadedBytes() + " / " + event.getTotalBytes();
// cc.log("updateCb -> " + event.getDownloadedBytes() + " / " + event.getTotalBytes());
// var msg: string = event.getMessage();
// if (msg) {
// // this.panel.info.string = 'Updated file: ' + msg;
// cc.log("updateCb -> Updated file: " + msg);
// console.log("updateCb -> " + event.getPercent() / 100 + "% : " + msg);
// }
var msg: string = event.getMessage();
if (this._onFileProgress) {
this._onFileProgress(event.getDownloadedBytes(), event.getTotalBytes(), msg ? msg : "");
}
break;
case jsb.EventAssetsManager.ERROR_DOWNLOAD_MANIFEST:
case jsb.EventAssetsManager.ERROR_PARSE_MANIFEST:
// this.panel.info.string = 'Fail to download manifest file, HUD skipped.';
console.error("updateCb -> Fail to download manifest file, HUD skipped.");
failed = true;
break;
case jsb.EventAssetsManager.ALREADY_UP_TO_DATE:
// this.panel.info.string = 'Already up to date with the latest remote version.';
console.error("updateCb -> Already up to date with the latest remote version.");
failed = true;
break;
case jsb.EventAssetsManager.UPDATE_FINISHED:
// this.tipsLabel.string = "更新完成. " + event.getMessage();
// cc.log("updateCb -> 更新完成. " + event.getMessage());
this._updateingData = new Enum_Loading.UpdateingDataObj(true);
needRestart = true;
break;
case jsb.EventAssetsManager.UPDATE_FAILED:
// this.panel.info.string = 'Update failed. ' + event.getMessage();
console.error("updateCb -> Update failed. " + event.getMessage());
// this.panel.retryBtn.active = true;
this._canRetry = true;
this._updateingData = new Enum_Loading.UpdateingDataObj(false);
this._updating = false;
break;
case jsb.EventAssetsManager.ERROR_UPDATING:
// this.panel.info.string = 'Asset update error: ' + event.getAssetId() + ', ' + event.getMessage();
console.error("updateCb -> Asset update error: " + event.getAssetId() + ", " + event.getMessage());
break;
case jsb.EventAssetsManager.ERROR_DECOMPRESS:
// this.panel.info.string = event.getMessage();
console.error("updateCb -> " + event.getMessage());
break;
default:
break;
}
if (failed) {
this._am.setEventCallback(null);
this._updateListener = null;
this._updating = false;
}
// 測試先不restart 之後看情況
// if (needRestart) {
// this._am.setEventCallback(null);
// this._updateListener = null;
// // Prepend the manifest's search path
// var searchPaths: string[] = jsb.fileUtils.getSearchPaths();
// // var newPaths = this._am.getLocalManifest().getSearchPaths();
// // cc.log("newPath."+JSON.stringify(newPaths));
// // Array.prototype.unshift.apply(searchPaths, newPaths);
// cc.sys.localStorage.setItem("HUDSearchPaths", JSON.stringify(searchPaths));
// jsb.fileUtils.setSearchPaths(searchPaths);
// cc.audioEngine.stopAll();
// cc.game.restart();
// }
}
public *RetryDownLoadFailedAssets(): IterableIterator<any> {
if (!this._updating && this._canRetry) {
this._updateingData = null;
// this.panel.retryBtn.active = false;
this._canRetry = false;
// this.panel.info.string = 'Retry failed Assets...';
// cc.log("retry -> Retry failed Assets...");
this._am.downloadFailedAssets();
while (this._updateingData === null) {
yield null;
}
return this._updateingData;
} else {
console.error(`retry -> error updating: ${this._updating}, canRetry: ${this._canRetry}`);
this._updateingData = new Enum_Loading.UpdateingDataObj(false);
}
}
private _bytesToSize(bytes: number): string {
if (bytes === 0) {
return "0 B";
}
let k: number = 1024;
let sizes: string[] = ["B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
let i: number = Math.floor(Math.log(bytes) / Math.log(k));
return (bytes / Math.pow(k, i)).toPrecision(3) + " " + sizes[i];
}
protected onDestroy(): void {
if (this._updateListener) {
this._am.setEventCallback(null);
this._updateListener = null;
}
}
}