2022-09-03 23:53:06 +08:00

352 lines
13 KiB
TypeScript

import { CoroutineV2 } from "../Engine/CatanEngine/CoroutineV2/CoroutineV2";
import UpdatePanel from "../UpdatePanel";
import BusinessTypeSetting from "../_BusinessTypeSetting/BusinessTypeSetting";
import Enum_HUDM from "./Enum_HUDM";
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 private 屬性
private _updatePanel: UpdatePanel;
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_HUDM.NeedUpdateDataObj = null;
private _updateingData: Enum_HUDM.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;
}
HUDM._instance = this;
this._updatePanel = params[0];
let packageUrl: string = `https://jianmiau.tk/Resources/App/JMKA/update/remote-assets/${BusinessTypeSetting.COMPILE_VERSION}`;
this._customManifest = JSON.stringify({
"packageUrl": packageUrl,
"remoteManifestUrl": `${packageUrl}/project.manifest`,
"remoteVersionUrl": `${packageUrl}/version.json`,
"version": "0.0",
});
this._storagePath = `${(jsb.fileUtils ? jsb.fileUtils.getWritablePath() : "./")}${this._path}`;
this._versionCompareHandle = function (versionA: string, versionB: string): number {
// console.log("Ver A " + versionA + "VerB " + versionB);
let vA: string[] = versionA.split(".");
let vB: string[] = versionB.split(".");
// 長度不相等,則進行更新
if (vA.length !== vB.length) {
return -1;
}
for (let i: number = 0; i < vA.length; ++i) {
let a: number = +vA[i];
let b: number = +vB[i] || 0;
if (a === b) {
// 數字相同,則跳過
continue;
} else {
// 數字不同,則進行更新
return -1;
}
}
// 長度相等且數字相等,則不更新
return 0;
};
this._initAssetManaget();
}
private _initAssetManaget(): void {
let self: this = this;
//
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.
let compressed: any = asset.compressed;
// Retrieve the correct md5 value.
let expectedMD5: string = asset.md5;
// asset.path is relative path and path is absolute.
let relativePath: string = asset.path;
// The size of asset file, but this value could be absent.
let size: any = asset.size;
if (compressed) {
self._updatePanel.info.string = "Verification passed : " + relativePath;
// console.log("onLoad -> Verification passed : " + relativePath);
return true;
} else {
self._updatePanel.info.string = "Verification passed : " + relativePath + " (" + expectedMD5 + ")";
// console.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._updatePanel.info.string = "Max concurrent tasks count have been limited to 2";
// console.log("onLoad -> Max concurrent tasks count have been limited to 10");
}
}
//#endregion
public *CheckUpdate(): IterableIterator<any> {
this._needUpdateData = null;
if (this._updating) {
this._updatePanel.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;
}
return this._needUpdateData;
}
private checkCb(event: jsb.EventAssetsManager): void {
let failed: boolean = false;
switch (event.getEventCode()) {
case jsb.EventAssetsManager.ERROR_NO_LOCAL_MANIFEST:
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:
console.error("checkCb -> Fail to download manifest file, HUD skipped.");
failed = true;
break;
case jsb.EventAssetsManager.ALREADY_UP_TO_DATE:
console.log("checkCb -> Already up to date with the latest remote version.");
this._needUpdateData = new Enum_HUDM.NeedUpdateDataObj(false);
break;
case jsb.EventAssetsManager.NEW_VERSION_FOUND:
this._updatePanel.checkBtn.active = false;
this._updatePanel.fileProgress.progress = 0;
this._updatePanel.byteProgress.progress = 0;
this._updatePanel.info.string = "發現新版本,請嘗試更新。 " + this._bytesToSize(event.getTotalBytes());
console.log("checkCb -> New version found, please try to update." + event.getTotalBytes());
this._needUpdateData = new Enum_HUDM.NeedUpdateDataObj(true, this._bytesToSize(event.getTotalBytes()));
break;
default:
return;
}
this._am.setEventCallback(null);
this._checkListener = null;
this._updating = false;
if (failed) {
this._needUpdateData = new Enum_HUDM.NeedUpdateDataObj(false, null);
}
}
public *HUD(onFileProgress?: (finish: number, total: number, item: string) => void): IterableIterator<any> {
this._updatePanel.updateBtn.active = false;
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_HUDM.UpdateingDataObj(false);
}
}
private _updateCb(event: jsb.EventAssetsManager): void {
let self: this = this;
let needRestart: boolean = false;
let failed: boolean = false;
switch (event.getEventCode()) {
case jsb.EventAssetsManager.ERROR_NO_LOCAL_MANIFEST:
this._updatePanel.info.string = "No local manifest file found, HUD skipped.";
console.log("updateCb -> No local manifest file found, HUD skipped.");
failed = true;
break;
case jsb.EventAssetsManager.UPDATE_PROGRESSION:
this._updatePanel.byteProgress.progress = event.getPercent();
this._updatePanel.fileProgress.progress = event.getPercentByFile();
this._updatePanel.fileLabel.string = event.getDownloadedFiles() + " / " + event.getTotalFiles();
// this.tipsLabel.string = event.getDownloadedBytes() + " / " + event.getTotalBytes();
// console.log("updateCb -> " + event.getDownloadedBytes() + " / " + event.getTotalBytes());
// let msg: string = event.getMessage();
// if (msg) {
// this._updatePanel.info.string = 'Updated file: ' + msg;
// console.log("updateCb -> Updated file: " + msg);
// console.log("updateCb -> " + event.getPercent() / 100 + "% : " + msg);
// }
let 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._updatePanel.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._updatePanel.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();
console.log("updateCb -> 更新完成. " + event.getMessage());
this._updateingData = new Enum_HUDM.UpdateingDataObj(true);
needRestart = true;
break;
case jsb.EventAssetsManager.UPDATE_FAILED:
this._updatePanel.info.string = "Update failed. " + event.getMessage();
console.error("updateCb -> Update failed. " + event.getMessage());
this._updatePanel.retryBtn.active = true;
this._canRetry = true;
this._updateingData = new Enum_HUDM.UpdateingDataObj(false);
this._updating = false;
break;
case jsb.EventAssetsManager.ERROR_UPDATING:
this._updatePanel.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._updatePanel.info.string = event.getMessage();
console.error("updateCb -> " + event.getMessage());
break;
default:
break;
}
if (failed) {
this._am.setEventCallback(null);
this._updateListener = null;
this._updating = false;
}
if (needRestart) {
let AsyncFunction: () => IterableIterator<any> = function* (): IterableIterator<any> {
self._updatePanel.info.string = "更新完成 即將重啟";
// 卡個一幀不然都看不到100%的畫面
yield CoroutineV2.WaitTime(5 / cc.game.getFrameRate()).Start();
self._am.setEventCallback(null);
self._updateListener = null;
// Prepend the manifest's search path
let searchPaths: string[] = jsb.fileUtils.getSearchPaths();
let newPaths: [string] = self._am.getLocalManifest().getSearchPaths();
console.log(JSON.stringify(newPaths));
Array.prototype.unshift.apply(searchPaths, newPaths);
// This value will be retrieved and appended to the default search path during game startup,
// please refer to samples/js-tests/main.js for detailed usage.
// !!! Re-add the search paths in main.js is very important, otherwise, new scripts won't take effect.
cc.sys.localStorage.setItem("HotUpdateSearchPaths", JSON.stringify(searchPaths));
jsb.fileUtils.setSearchPaths(searchPaths);
cc.audioEngine.stopAll();
cc.game.restart();
};
CoroutineV2.Single(AsyncFunction()).Start();
}
}
public *RetryDownLoadFailedAssets(): IterableIterator<any> {
if (!this._updating && this._canRetry) {
this._updateingData = null;
this._updatePanel.retryBtn.active = false;
this._canRetry = false;
this._updatePanel.info.string = "Retry failed Assets...";
console.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_HUDM.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;
}
}
}