diff --git a/src/hotupdate/HotUpdate.ts b/src/hotupdate/HotUpdate.ts new file mode 100644 index 0000000..e3f3efb --- /dev/null +++ b/src/hotupdate/HotUpdate.ts @@ -0,0 +1,326 @@ +/** + * @Author: Gongxh + * @Date: 2025-04-19 + * @Description: 热更新实例 + */ + +import { game, native, sys } from "cc"; +import { ICheckUpdatePromiseResult, IPromiseResult } from "../interface/PromiseResult"; +import { ReadNetFile } from "../net/nettools/ReadNetFile"; +import { debug, log } from "../tool/log"; +import { Utils } from "../tool/Utils"; +import { HotUpdateManager } from "./HotUpdateManager"; + +interface IHotUpdateConfig { + packageUrl: string; + remoteManifestUrl: string; + remoteVersionUrl: string; + version: string; +} + +export interface IManifestResult extends IPromiseResult { + manifest?: IHotUpdateConfig; +} + +export enum HotUpdateCode { + /** 成功 */ + Succeed = 0, + /** 出错了 */ + Error = -1, + /** 平台不支持 不需要热更新 */ + PlatformNotSupported = -1000, + /** 未初始化 */ + NotInitialized = -1001, + /** 是最新版本 */ + LatestVersion = -1002, + /** 更新中 */ + Updating = -1003, + /** 加载本地manifest失败 */ + LoadManifestFailed = -1004, + /** 下载manifest文件失败 */ + ParseManifestFailed = -1005, + + /** 下载version.manifest失败 */ + LoadVersionFailed = -1006, + /** 解析version.manifest失败 */ + ParseVersionFailed = -1007, + + + /** 更新失败 需要重试 */ + UpdateFailed = -1008, + /** 更新错误 */ + UpdateError = -1009, + /** 解压错误 */ + DecompressError = -1010, +} + +const TAG = "hotupdate:"; +export class HotUpdate { + /** 资源管理器 */ + private _am: native.AssetsManager = null; + /** 更新进度回调 */ + private _progress: (kb: number, total: number) => void = null; + private _complete: (code: HotUpdateCode, message: string) => void = null; + + public get resVersion(): string { + return this._am?.getLocalManifest()?.getVersion() || "0"; + } + + /** 获取 version.manifest 文件的远程地址 */ + private get versionUrl(): string { + return this._am?.getLocalManifest()?.getVersionFileUrl() || ""; + } + + constructor() { + let writablePath = HotUpdateManager.getInstance().writablePath; + let manifestUrl = HotUpdateManager.getInstance().manifestUrl; + + // 创建 am 对象 + this._am = new native.AssetsManager(manifestUrl, writablePath, Utils.compareVersion); + this._am?.setVerifyCallback(this._verifyCallback); + HotUpdateManager.getInstance().resVersion = this.resVersion; + } + + /** 重试失败的资源 */ + public retryUpdate(): void { + this._am.downloadFailedAssets(); + } + + /** + * 检查是否存在热更新 + * 提供一个对外的方法检查是否存在热更新 + * @return {Promise} + */ + public checkUpdate(): Promise { + let localManifest: IHotUpdateConfig = null; + return new Promise((resolve, reject) => { + this.readLocalManifest().then(res => { + log(`${TAG} 读取本地manifest文件结果:${JSON.stringify(res)}`); + if (res.code === HotUpdateCode.Succeed) { + localManifest = res.manifest; + return this.loadRemoteVersionManifest(); + } else { + throw res; + } + }).then(res => { + log(`${TAG} 读取远程version.manifest文件结果:${JSON.stringify(res)}`); + // 获取远程version.manifest文件内容的结果 + if (res.code === HotUpdateCode.Succeed) { + return this.refreshLocalManifest(localManifest, res.manifest); + } else { + throw res; + } + }).then(res => { + log(`${TAG} 刷新本地manifest文件结果:${JSON.stringify(res)}`); + if (res.code === HotUpdateCode.Succeed) { + return this.startCheckUpdate(); + } else { + // 已经是最新版本了 + throw res; + } + }).then(res => { + log(`${TAG} 检查更新结果:${JSON.stringify(res)}`); + resolve(res); + }).catch(res => { + resolve(res); + }); + }); + } + + /** + * 开始热更新 + * @param res.skipCheck 是否跳过检查更新 + * @param res.progress 更新进度回调 kb: 已下载的资源大小, total: 总资源大小 (kb) + * @param res.complete 更新结束回调 根据错误码判断 跳过还是重试失败资源 + */ + public startUpdate(res: { skipCheck?: boolean, progress: (kb: number, total: number) => void, complete: (code: HotUpdateCode, message: string) => void }): void { + this._progress = res.progress; + this._complete = res.complete; + + if (res.skipCheck) { + this.startUpdateTask(); + } else { + this.checkUpdate().then((res) => { + if (res.code === HotUpdateCode.Succeed) { + this.startUpdateTask(); + } else { + this._complete(res.code, res.message); + } + }).catch((err) => { + this._complete(HotUpdateCode.Error, JSON.stringify(err)); + }); + } + } + + private startUpdateTask(): void { + this._am.setEventCallback((event: native.EventAssetsManager) => { + let eventCode = event.getEventCode(); + debug(`${TAG} 更新回调code:${eventCode}`); + switch (eventCode) { + case native.EventAssetsManager.UPDATE_PROGRESSION: { + let bytes = event.getDownloadedBytes() / 1024; + let total = event.getTotalBytes() / 1024; + this._progress(bytes, total); + break; + } + case native.EventAssetsManager.UPDATE_FINISHED: { + // 更新完成 自动重启 + this._am.setEventCallback(null); + + // Prepend the manifest's search path + let searchPaths = native.fileUtils.getSearchPaths(); + log(`${TAG} 当前搜索路径:${JSON.stringify(searchPaths)}`); + + let newPaths = this._am.getLocalManifest().getSearchPaths(); + log(`${TAG} 新搜索路径:${JSON.stringify(newPaths)}`); + + Array.prototype.unshift.apply(searchPaths, newPaths); + sys.localStorage.setItem('hotupdate::version', HotUpdateManager.getInstance().version); + sys.localStorage.setItem('hotupdate::searchpaths', JSON.stringify(searchPaths)); + native.fileUtils.setSearchPaths(searchPaths); + + // 0.5秒后 自动重启游戏 + setTimeout(() => { game.restart(); }, 500); + break; + } + case native.EventAssetsManager.UPDATE_FAILED: { + // 更新失败了, 等待重试 + this._complete(HotUpdateCode.UpdateFailed, event.getMessage()); + break; + } + case native.EventAssetsManager.ERROR_UPDATING: { + // 更新出错了, 一般是开发中的问题, 重启游戏 + this._complete(HotUpdateCode.UpdateError, event.getMessage()); + break; + } + case native.EventAssetsManager.ERROR_DECOMPRESS: { + // 解压出错了, 一般是开发中的问题, 重启游戏 + this._complete(HotUpdateCode.DecompressError, event.getMessage()); + break; + } + default: + break; + } + }); + this._am.update(); + } + + /** 验证资源 */ + private _verifyCallback(path: string, asset: native.ManifestAsset): boolean { + // 资源是否被压缩, 如果压缩我们不需要检查它的md5值 + let compressed = asset.compressed; + if (compressed) { + return true; + } + // 预期的md5 + let expectedMD5 = asset.md5; + // 资源大小 + let size = asset.size; + // 验证资源md5 + log(`${TAG} 记录的md5:${expectedMD5} 文件大小:${size} 文件相对路径:${asset.path} 绝对路径:${path}`); + return true; + } + + /** 读取本地的project.manifest文件 */ + private readLocalManifest(): Promise { + return new Promise((resolve, reject) => { + if (!this._am) { + reject({ code: HotUpdateCode.LoadManifestFailed, message: "读取本地project.manifest文件失败" }); + return; + } + let content = native.fileUtils.getStringFromFile(HotUpdateManager.getInstance().manifestUrl); + if (content) { + resolve({ code: HotUpdateCode.Succeed, message: "读取本地project.manifest文件成功", manifest: JSON.parse(content) }); + } else { + reject({ code: HotUpdateCode.LoadManifestFailed, message: "读取本地project.manifest文件失败" }); + } + }); + } + + /** 读取远程version.manifest文件内容 */ + private loadRemoteVersionManifest(): Promise { + return new Promise((resolve) => { + new ReadNetFile({ + url: this.versionUrl, + timeout: 5, + responseType: "text", + onComplete: (data: string) => { + log(`${TAG} 下载hotconfig文件成功`); + if (Utils.isJsonString(data)) { + resolve({ code: HotUpdateCode.Succeed, message: "读取远程version.manifest文件成功", manifest: JSON.parse(data) }); + } else { + log(`${TAG} 远程version.manifest文件格式错误`); + resolve({ code: HotUpdateCode.ParseVersionFailed, message: "远程version.manifest文件格式错误" }); + } + }, + onError: (code: number, message: string) => { + log(`${TAG} 读取远程version.manifest文件失败`, code, message); + resolve({ code: HotUpdateCode.LoadVersionFailed, message: "读取远程version.manifest文件失败" }); + } + }); + }); + } + + /** 替换project.manifest中的内容 并刷新本地manifest */ + private refreshLocalManifest(manifest: IHotUpdateConfig, versionManifest: IHotUpdateConfig): Promise { + return new Promise((resolve) => { + if (Utils.compareVersion(manifest.version, versionManifest.version) >= 0) { + resolve({ code: HotUpdateCode.LatestVersion, message: "已是最新版本" }); + } else { + // 替换manifest中的内容 + manifest.remoteManifestUrl = versionManifest.remoteManifestUrl; + manifest.remoteVersionUrl = versionManifest.remoteVersionUrl; + manifest.packageUrl = versionManifest.packageUrl; + + // 注册本地manifest根目录 + let manifestRoot = ""; + let manifestUrl = HotUpdateManager.getInstance().manifestUrl; + let found = manifestUrl.lastIndexOf("/"); + if (found === -1) { + found = manifestUrl.lastIndexOf("\\"); + } + if (found !== -1) { + manifestRoot = manifestUrl.substring(0, found + 1); + } + this._am.getLocalManifest().parseJSONString(JSON.stringify(manifest), manifestRoot); + log(TAG + "manifest root:" + this._am.getLocalManifest().getManifestRoot()); + log(TAG + "manifest packageUrl:" + this._am.getLocalManifest().getPackageUrl()); + log(TAG + "manifest version:" + this._am.getLocalManifest().getVersion()); + log(TAG + "manifest versionFileUrl:" + this._am.getLocalManifest().getVersionFileUrl()); + log(TAG + "manifest manifestFileUrl:" + this._am.getLocalManifest().getManifestFileUrl()); + resolve({ code: HotUpdateCode.Succeed, message: "更新热更新配置成功" }); + } + }); + } + + /** 调用cc的接口检测更新 */ + private startCheckUpdate(): Promise { + return new Promise((resolve) => { + // 设置回调 + this._am.setEventCallback((event: native.EventAssetsManager) => { + let eventCode = event.getEventCode(); + log(`${TAG} 检查更新回调code:${eventCode}`); + switch (eventCode) { + case native.EventAssetsManager.ERROR_DOWNLOAD_MANIFEST: + this._am.setEventCallback(null); + resolve({ code: HotUpdateCode.LoadManifestFailed, message: "检查更新时下载manifest文件失败", needUpdate: false, size: 0 }); + return; + case native.EventAssetsManager.ERROR_PARSE_MANIFEST: + this._am.setEventCallback(null); + resolve({ code: HotUpdateCode.ParseManifestFailed, message: "检查更新时解析manifest文件失败", needUpdate: false, size: 0 }); + return; + case native.EventAssetsManager.ALREADY_UP_TO_DATE: + this._am.setEventCallback(null); + resolve({ code: HotUpdateCode.LatestVersion, message: "已是最新版本", needUpdate: false, size: 0 }); + return; + case native.EventAssetsManager.NEW_VERSION_FOUND: + // 发现新版本 + this._am.setEventCallback(null); + resolve({ code: HotUpdateCode.Succeed, message: "发现新版本", needUpdate: true, size: this._am.getTotalBytes() / 1024 }); + return; + } + }); + this._am.checkUpdate(); + }); + } +} diff --git a/src/hotupdate/HotUpdateManager.ts b/src/hotupdate/HotUpdateManager.ts index 32c89e3..5a97110 100644 --- a/src/hotupdate/HotUpdateManager.ts +++ b/src/hotupdate/HotUpdateManager.ts @@ -4,10 +4,11 @@ * @Description: 热更新管理器 */ -import { Asset, game, native, sys } from "cc"; -import { Platform } from "../global/Platform"; -import { log, warn } from "../tool/log"; -import { Utils } from "../tool/Utils"; +import { native } from "cc"; +import { ICheckUpdatePromiseResult } from "../interface/PromiseResult"; +import { Platform } from "../kunpocc"; +import { log } from "../tool/log"; +import { HotUpdate, HotUpdateCode } from "./HotUpdate"; const TAG = "hotupdate:"; @@ -19,37 +20,73 @@ export class HotUpdateManager { } return HotUpdateManager.instance; } - + /** 是否初始化了 */ + private _isInitialized: boolean = false; + /** 本地manifest路径 */ + private _manifestUrl: string = ''; /** 版本号 */ private _version: string = ''; + + /** 资源版本号 */ + private _resVersion: string = null; /** 可写路径 */ private _writablePath: string = ''; - /** 资源管理器 */ - private _am: native.AssetsManager = null; /** 是否正在更新 或者 正在检查更新 */ private _updating: boolean = false; - /** 检查更新的回调 */ - private _checkSucceed: (need: boolean, size: number) => void = null; - private _checkFail: (code: number, message: string) => void = null; + /** 更新实例 只有更新的时候初始化 检查更新不赋值 */ + private _hotUpdate: HotUpdate = null; + + /** + * 热更新文件存放的可写路径 + */ + public get writablePath(): string { + return this._writablePath; + } + + /** + * 本地manifest路径 + */ + public get manifestUrl(): string { + return this._manifestUrl; + } + + /** + * 传入的游戏版本号 + */ + public get version(): string { + return this._version; + } + + /** + * 获取资源版本号, 须初始化成功后再使用 + * @return 资源版本号 默认值 ‘0’ + */ + public get resVersion(): string { + if (this._resVersion === null) { + this._resVersion = new HotUpdate().resVersion; + } + return this._resVersion; + } + + public set resVersion(version: string) { + if (this._resVersion === null) { + this._resVersion = version; + } + } - /** 更新回调 */ - private _updateProgress: (kb: number, total: number) => void = null; - private _updateFail: (code: number, message: string) => void = null; - private _updateError: (code: number, message: string) => void = null; /** * 1. 初始化热更新管理器 - * @param manifest 传入manifest文件 - * @param version 传入游戏版本号 eg: 1.0.0 + * @param manifestUrl 传入本地manifest文件地址 资源的assets.nativeUrl + * @param version 游戏版本号 eg: 1.0.0 */ - public init(manifest: Asset, version: string): void { - if (!Platform.isNativeMobile) { - return; - } - if (this._am) { - warn(`${TAG}请勿重复初始化`); + public init(manifestUrl: string, version: string): void { + if (this._isInitialized) { + log(`${TAG} 热更新管理器不需要重复初始化`); return; } + this._isInitialized = true; + this._manifestUrl = manifestUrl; this._version = version; let writablePath = native?.fileUtils?.getWritablePath() || ""; @@ -58,184 +95,74 @@ export class HotUpdateManager { } this._writablePath = `${writablePath}hot-update/${version}/`; log(`${TAG}可写路径:${this._writablePath}`); - - // 创建 am 对象 - this._am = native.AssetsManager.create("", this._writablePath); - this._am.setVersionCompareHandle(Utils.compareVersion); - this._am.setVerifyCallback(this._verifyCallback); - // 加载本地的 manifest - log(`${TAG} 加载本地的 manifest:${manifest.nativeUrl}`); - this._am.loadLocalManifest(manifest.nativeUrl); } /** - * 2. 检查是否有新的热更版本 - * @param res.succeed.need 是否需要更新 - * @param res.succeed.size 需要更新的资源大小 (KB) - * - * @param res.fail 检查失败的回调 - * @param res.fail.code - * -1000: 未初始化 - * -1001: 正在更新或者正在检查更新 - * -1002: 本地manifest文件错误 - * -1004: 解析远程manifest文件失败 + * 检查是否存在热更新 + * 提供一个对外的方法检查是否存在热更新 + * @return {Promise} */ - public checkUpdate(res: { succeed: (need: boolean, size: number) => void, fail: (code: number, message: string) => void }): void { - this._checkSucceed = res.succeed; - this._checkFail = res.fail; - if (this._updating) { - res.fail(-1001, "正在更新或者正在检查更新"); - return; - } - if (!Platform.isNativeMobile) { - res.succeed(false, 0); - return; - } - if (!this._am) { - res.fail(-1000, "未初始化, 需要先调用init方法"); - return; - } - this._updating = true; - // 设置回调 - this._am.setEventCallback(this._checkCb.bind(this)); - // 检查更新 - this._am.checkUpdate(); + public checkUpdate(): Promise { + return new Promise((resolve, reject) => { + if (!Platform.isNativeMobile) { + resolve({ code: HotUpdateCode.PlatformNotSupported, message: "当前平台不需要热更新" }); + return; + } + if (!this._isInitialized) { + resolve({ code: HotUpdateCode.NotInitialized, message: "未初始化, 需要先调用init方法" }); + return; + } + if (this._updating) { + resolve({ code: HotUpdateCode.Updating, message: "正在更新或者正在检查更新中" }); + return; + } + this._updating = true; + new HotUpdate().checkUpdate().then((res) => { + this._updating = false; + resolve(res); + }).catch((err) => { + this._updating = false; + resolve({ code: HotUpdateCode.Error, message: JSON.stringify(err) }); + }); + }); } /** - * 3. 开始热更新 + * 开始热更新 + * @param res.skipCheck 是否跳过检查更新 * @param res.progress 更新进度回调 kb: 已下载的资源大小, total: 总资源大小 (kb) - * @param res.fail 更新失败 可以重试 - * @param res.fail.code 更新失败错误码 - * -10001: 更新失败 需要重试 - * @param res.error 更新错误 无法重试 - * @param res.error.code 更新错误错误码 - * -1000: 未初始化 - * -1001: 正在更新或者正在检查更新 - * -10002: 资源更新错误 - * -10003: 解压错误 + * @param res.complete 更新结束回调 根据错误码判断 跳过还是重试失败资源 */ - public startUpdate(res: { - progress: (kb: number, total: number) => void, - fail: (code: number, message: string) => void, - error: (code: number, message: string) => void - }): void { - this._updateProgress = res.progress; - this._updateFail = res.fail; - this._updateError = res.error; - - log(`${TAG} 开始热更新`); - if (this._updating) { - res.error(-1001, "正在更新或者正在检查更新"); + public startUpdate(res: { skipCheck: boolean, progress: (kb: number, total: number) => void, complete: (code: HotUpdateCode, message: string) => void }): void { + if (!Platform.isNativeMobile) { + res.complete(HotUpdateCode.PlatformNotSupported, "当前平台不需要热更新"); return; } - if (!this._am) { - res.error(-1000, "未初始化, 需要先调用init方法"); + if (!this._isInitialized) { + res.complete(HotUpdateCode.NotInitialized, "未初始化, 需要先调用init方法"); + return; + } + if (this._updating) { + res.complete(HotUpdateCode.Updating, "正在更新或者正在检查更新"); return; } this._updating = true; - this._am.setEventCallback(this._updateCb.bind(this)); - this._am.update(); + this._hotUpdate = new HotUpdate(); + this._hotUpdate.startUpdate({ + skipCheck: res.skipCheck, + progress: res.progress, + complete: (code: HotUpdateCode, message: string) => { + this._updating = false; + res.complete(code, message); + } + }); } /** 重试失败的资源 */ public retryUpdate(): void { - this._am.downloadFailedAssets(); - } - - /** 检查更新的回调 */ - private _checkCb(event: native.EventAssetsManager) { - let eventCode = event.getEventCode(); - log(`${TAG} 检查更新回调code:${eventCode}`); - this._updating = false; - switch (eventCode) { - case native.EventAssetsManager.ERROR_NO_LOCAL_MANIFEST: - this._checkFail(-1002, "本地没有manifest文件"); - break; - case native.EventAssetsManager.ERROR_DOWNLOAD_MANIFEST: - // this._checkFail(-1003, "下载manifest文件失败"); - this._checkSucceed(false, 0); - break; - case native.EventAssetsManager.ERROR_PARSE_MANIFEST: - this._checkFail(-1004, "解析远程manifest文件失败"); - break; - case native.EventAssetsManager.ALREADY_UP_TO_DATE: - this._checkSucceed(false, 0); - break; - case native.EventAssetsManager.NEW_VERSION_FOUND: - // 发现新版本 - this._checkSucceed(true, this._am.getTotalBytes() / 1024); - break; - default: - return; + if (!this._hotUpdate) { + throw new Error(`${TAG} 使用前 必须使用过startUpdate方法`); } - this._am.setEventCallback(null); - } - - /** 更新的回调 */ - private _updateCb(event: native.EventAssetsManager) { - let eventCode = event.getEventCode(); - log(`${TAG} 更新回调code:${eventCode}`); - let needRestart = false; - switch (eventCode) { - case native.EventAssetsManager.UPDATE_PROGRESSION: - let bytes = event.getDownloadedBytes() / 1024; - let total = event.getTotalBytes() / 1024; - this._updateProgress(bytes, total); - break; - case native.EventAssetsManager.UPDATE_FINISHED: - // 更新完成 自动重启 - needRestart = true; - break; - case native.EventAssetsManager.UPDATE_FAILED: - this._updating = false; - this._updateFail(-10001, event.getMessage()); - break; - case native.EventAssetsManager.ERROR_UPDATING: - this._updating = false; - this._updateError(-10002, event.getMessage()); - break; - case native.EventAssetsManager.ERROR_DECOMPRESS: - this._updating = false; - this._updateError(-10003, event.getMessage()); - break; - default: - break; - } - if (needRestart) { - this._am.setEventCallback(null); - - // Prepend the manifest's search path - let searchPaths = native.fileUtils.getSearchPaths(); - log(`${TAG} 当前搜索路径:${JSON.stringify(searchPaths)}`); - - let newPaths = this._am.getLocalManifest().getSearchPaths(); - log(`${TAG} 新搜索路径:${JSON.stringify(newPaths)}`); - - Array.prototype.unshift.apply(searchPaths, newPaths); - sys.localStorage.setItem('hotupdate::version', this._version); - sys.localStorage.setItem('hotupdate::searchpaths', JSON.stringify(searchPaths)); - native.fileUtils.setSearchPaths(searchPaths); - - // 重启游戏 - setTimeout(() => { - game.restart() - }, 500); - } - } - - private _verifyCallback(path: string, asset: native.ManifestAsset): boolean { - // 资源是否被压缩, 如果压缩我们不需要检查它的md5值 - let compressed = asset.compressed; - if (compressed) { - return true; - } - // 预期的md5 - let expectedMD5 = asset.md5; - // 资源大小 - let size = asset.size; - // 验证资源md5 - log(`${TAG} 记录的md5:${expectedMD5} 文件大小:${size} 文件相对路径:${asset.path} 绝对路径:${path}`); - return true; + this._hotUpdate.retryUpdate(); } } diff --git a/src/interface/PromiseResult.ts b/src/interface/PromiseResult.ts new file mode 100644 index 0000000..241cffe --- /dev/null +++ b/src/interface/PromiseResult.ts @@ -0,0 +1,19 @@ +/** + * @Author: Gongxh + * @Date: 2025-04-18 + * @Description: 通用的 Promise 结果 + */ + +export interface IPromiseResult { + /** 0:成功 其他:失败 */ + code: number; + /** 失败信息 */ + message: string; +} + +export interface ICheckUpdatePromiseResult extends IPromiseResult { + /** 是否需要更新 */ + needUpdate?: boolean; + /** 需要更新的资源大小 (KB) */ + size?: number; +} \ No newline at end of file diff --git a/src/kunpocc.ts b/src/kunpocc.ts index 25f0df2..85b8a00 100644 --- a/src/kunpocc.ts +++ b/src/kunpocc.ts @@ -4,6 +4,7 @@ export { GlobalTimer } from "./global/GlobalTimer"; export { enableDebugMode, FrameConfig, KUNPO_DEBUG } from "./global/header"; export { Platform, PlatformType } from "./global/Platform"; export { Screen } from "./global/Screen"; +export * from "./interface/PromiseResult"; /** tool */ export { Binary } from "./tool/Binary"; @@ -24,6 +25,9 @@ export { IHttpResponse } from "./net/http/IHttpResponse"; /** Socket */ export { Socket } from "./net/socket/Socket"; +/** 读取网络文件 */ +export { ReadNetFile } from "./net/nettools/ReadNetFile"; + /** 四叉树 */ export { Box } from "./quadtree/Box"; export { Circle } from "./quadtree/Circle"; @@ -78,6 +82,7 @@ export { ConditionAnyNode } from "./condition/node/ConditionAnyNode"; export { ConditionBase } from "./condition/node/ConditionBase"; /** 热更新 */ +export { HotUpdateCode } from "./hotupdate/HotUpdate"; export { HotUpdateManager } from "./hotupdate/HotUpdateManager"; /** 小游戏 */ diff --git a/src/minigame/bytedance/BytedanceCommon.ts b/src/minigame/bytedance/BytedanceCommon.ts index b40563d..14b02b8 100644 --- a/src/minigame/bytedance/BytedanceCommon.ts +++ b/src/minigame/bytedance/BytedanceCommon.ts @@ -4,7 +4,6 @@ * @Description: 字节跳动小游戏工具类 */ -import { LaunchParams } from "@douyin-microapp/typings/types/app"; import { warn } from "../../tool/log"; import { IMiniCommon } from "../interface/IMiniCommon"; @@ -24,14 +23,14 @@ export class BytedanceCommon implements IMiniCommon { /** * 获取冷启动参数 */ - public getLaunchOptions(): LaunchParams { + public getLaunchOptions(): BytedanceMiniprogram.LaunchParams { return this._launchOptions; } /** * 获取热启动参数 */ - public getHotLaunchOptions(): LaunchParams { + public getHotLaunchOptions(): BytedanceMiniprogram.LaunchParams { warn("字节跳动小游戏未提供热启动参数获取方式,请在 onShow 中获取"); return null; } diff --git a/src/net/http/IHttpEvent.ts b/src/net/http/IHttpEvent.ts index 7127946..af7948a 100644 --- a/src/net/http/IHttpEvent.ts +++ b/src/net/http/IHttpEvent.ts @@ -8,7 +8,7 @@ import { IHttpResponse } from "./IHttpResponse"; export interface IHttpEvent { /** 名称 */ - name: string; + name?: string; /** 自定义参数 */ data?: any; /** 网络请求成功 */ diff --git a/src/net/nettools/ReadNetFile.ts b/src/net/nettools/ReadNetFile.ts new file mode 100644 index 0000000..9b436b1 --- /dev/null +++ b/src/net/nettools/ReadNetFile.ts @@ -0,0 +1,29 @@ +/** + * @Author: Gongxh + * @Date: 2025-04-18 + * @Description: 读取网络文件内容 + */ + +import { Time } from "../../tool/Time"; +import { HttpManager } from "../http/HttpManager"; +import { IHttpResponse } from "../http/IHttpResponse"; + +export class ReadNetFile { + constructor(res: { url: string, timeout: number, responseType: "text" | "json" | "arraybuffer", onComplete: (data: any) => void, onError: (code: number, message: string) => void }) { + // 地址上带时间戳参数 确保每次请求都到服务器上请求最新配置,而不是拿到上次请求的缓存数据 + let url = res.url; + if (url.indexOf("?") > -1) { + url += `&timeStamp=${Time.now()}`; + } else { + url += `?timeStamp=${Time.now()}`; + } + HttpManager.get(url, null, res.responseType, { + onComplete: (response: IHttpResponse) => { + res.onComplete(response.data); + }, + onError: (response: IHttpResponse) => { + res.onError(response.statusCode, response.message); + } + }, null, res.timeout || 6); + } +} \ No newline at end of file diff --git a/src/tool/Utils.ts b/src/tool/Utils.ts index a037231..47a5386 100644 --- a/src/tool/Utils.ts +++ b/src/tool/Utils.ts @@ -36,4 +36,15 @@ export class Utils { return 0; } + /** + * 判断传入的字符串是否是json格式的字符串 + */ + public static isJsonString(str: string): boolean { + try { + JSON.parse(str); + return true; + } catch (e) { + return false; + } + } } \ No newline at end of file