JM_KA/assets/Script/HUD/HUDM.ts

449 lines
17 KiB
TypeScript
Raw Normal View History

2022-08-31 01:48:48 +00:00
import { CoroutineV2 } from "../Engine/CatanEngine/CoroutineV2/CoroutineV2";
2022-09-04 04:01:59 +00:00
import LocalStorageData from "../Engine/Data/LocalStorageData";
2022-08-31 01:48:48 +00:00
import UpdatePanel from "../UpdatePanel";
2022-09-03 15:53:06 +00:00
import BusinessTypeSetting from "../_BusinessTypeSetting/BusinessTypeSetting";
2022-08-31 01:48:48 +00:00
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;
2022-09-04 05:13:06 +00:00
private _isNewBundle: boolean = false;
2022-08-31 01:48:48 +00:00
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];
2022-09-04 05:13:06 +00:00
this._isNewBundle = params[1];
2022-09-04 04:01:59 +00:00
// let packageUrl: string = `https://jianmiau.tk/Resources/App/JMKA/update/remote-assets/${BusinessTypeSetting.COMPILE_VERSION}`;
let packageUrl: string = BusinessTypeSetting.UsePatch;
this.CheckCompileVersion();
this.CheckChangePatchUrl();
2022-08-31 01:48:48 +00:00
this._customManifest = JSON.stringify({
"packageUrl": packageUrl,
"remoteManifestUrl": `${packageUrl}/project.manifest`,
"remoteVersionUrl": `${packageUrl}/version.json`,
2022-09-04 04:01:59 +00:00
"version": "0.0.0",
2022-08-31 01:48:48 +00:00
});
this._storagePath = `${(jsb.fileUtils ? jsb.fileUtils.getWritablePath() : "./")}${this._path}`;
2022-09-04 04:01:59 +00:00
// 本地熱更目錄下已存在project.manifest則直接修改已存在的project.manifest
if (this._isChangeUrl) {
if (jsb.fileUtils.isFileExist(this._storagePath + "/project.manifest")) {
this._isChangeUrl = true;
this._modifyAppLoadUrlForManifestFile(this._storagePath, packageUrl);
}
}
2022-08-31 01:48:48 +00:00
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");
}
}
2022-09-04 04:01:59 +00:00
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);
jsb.fileUtils.writeStringToFile(afterString, path);
}
}
}
2022-08-31 01:48:48 +00:00
//#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;
}
2022-09-04 04:01:59 +00:00
let newBundleUrl: string = BusinessTypeSetting.UsePatch;
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;
}
2022-09-04 05:13:06 +00:00
if ((this._isChangeUrl || this._isNewBundle) && ((!this._needUpdateData.IsNeedUpdate && this._needUpdateData.TotalBytes !== "failed") || this._needUpdateData.TotalBytes === "0 B")) {
2022-09-04 04:01:59 +00:00
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();
}
}
}
2022-08-31 01:48:48 +00:00
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) {
2022-09-04 04:01:59 +00:00
this._needUpdateData = new Enum_HUDM.NeedUpdateDataObj(false, "failed");
2022-08-31 01:48:48 +00:00
}
}
public *HUD(onFileProgress?: (finish: number, total: number, item: string) => void): IterableIterator<any> {
2022-09-03 15:53:06 +00:00
this._updatePanel.updateBtn.active = false;
2022-08-31 01:48:48 +00:00
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;
}
}
2022-09-04 04:01:59 +00:00
//#region 清除資料
/** 判斷更改編譯版號.清除BUNDLE記錄 */
public CheckCompileVersion(): void {
let oldCompileVersion: string = LocalStorageData.Instance.CompileVersion;
let newCompileVersion: string = BusinessTypeSetting.COMPILE_VERSION;
if (oldCompileVersion && oldCompileVersion !== newCompileVersion) {
// this.ClearBundleData();
console.warn(`change compile version. ${oldCompileVersion} -> ${newCompileVersion}`);
}
LocalStorageData.Instance.CompileVersion = BusinessTypeSetting.COMPILE_VERSION;
}
/** 判斷更改PATCH環境.清除BUNDLE記錄 */
public CheckChangePatchUrl(): void {
let oldBundleUrl: string = LocalStorageData.Instance.BundleUrl;
let newBundleUrl: string = BusinessTypeSetting.UsePatch;
if (oldBundleUrl && oldBundleUrl !== newBundleUrl) {
// this.ClearBundleData();
console.warn(`change patch url. ${oldBundleUrl} -> ${newBundleUrl}`);
this._isChangeUrl = true;
}
LocalStorageData.Instance.BundleUrl = BusinessTypeSetting.UsePatch;
}
//#endregion
2022-08-31 01:48:48 +00:00
}