import { assetManager, game, sys } from "cc"; import { BYTEDANCE, DEBUG, DEV, EDITOR, WECHAT } from "cc/env"; import { RewardedVideoAd } from "./functions/RewardedVideoAd"; import { sdkConfig } from "./sdkConfig"; import GEMgr from "./GEMgr"; import { InterstitialAd } from "./functions/InterstitialAd"; import TTRecording from "./functions/TTRecording"; import { Share } from "./functions/Share"; import Vibrate from "./functions/VibrateMgr"; import { TTRank } from "./functions/TTRank"; import { Singleton } from "../../mx/tools/Singleton"; import { ItemNumType, StatisticsType, StatusType, TaskType } from "../game/GameData"; import { update } from "../../../extensions/tools/source/scene"; import { PfDiagnostics } from "../diagnostics/PfDiagnostics"; import { BattlePerformance } from "../ui/BattleGame/BattlePerformance"; import { MiniGameKind, MiniGamePlatform, MiniGameHostApi } from "./platform/MiniGamePlatform"; import { Physics2DGate } from "../game/Physics2DGate"; /** * SDK管理器 */ export class SDKManager extends Singleton { /** * 抖音头条录屏相关 */ private Recording: TTRecording = null; /** * 分享相关 */ Share: Share = new Share(); /** * 震动 */ Vibrate = Vibrate; /** * 当前平台 API(运行时宿主对象,含各平台扩展能力) */ pf: MiniGameHostApi | null = null; /** * 平台获取的查询参数 */ query = ""; showAd = true; upTestData = false; levelS: number[] = []; serverTime: number = 0; timer: number = 0; /** 平台 triggerGC 已禁用:主动 GC 会触发卡帧尖刺,改由引擎与内存压力节流应对 */ private _lastPfGcMs = 0; private static readonly PF_GC_MIN_LEVEL = 15; private static readonly PF_GC_COOLDOWN_MS = 120000; /** 是否允许调用平台 triggerGC(线上关闭) */ private static readonly PF_GC_ENABLED = false; /** 最近一次平台 onMemoryWarning 的 level 与时间(界面打开失败时附带上报) */ private _lastMemWarnLevel: number | null = null; private _lastMemWarnMs = 0; /** 激励视频嵌套计数(加载失败重试等场景) */ private _videoAdDepth = 0; /** 本次广告是否由 SDK 调用了 battle.pause('videoAd') */ private _videoAdBattlePausedBySdk = false; /** * 初始化 */ async init() { if (EDITOR || DEBUG || sys.isBrowser) { this.upTestData = true; return null; } MiniGamePlatform.init(); this.pf = MiniGamePlatform.pf; if (!this.pf) { return null; } if (MiniGamePlatform.kind !== MiniGameKind.Kuaishou) { this.initUpdate(); } this.pf.onShow(this.onPfShow.bind(this)); this.pf.onHide(this.onPfHide.bind(this)); this._bindGlobalErrorReporter(); this.serverTime = new Date().getTime(); RewardedVideoAd.init(sdkConfig.config.platformConf.video); InterstitialAd.init(sdkConfig.config.platformConf.interstitialAd); this.Share.init(sdkConfig.config.platformConf.shareData); this.openWXShareMenu() if (sys.platform == sys.Platform.BYTEDANCE_MINI_GAME) { this.Recording = TTRecording.Ins; this.Recording.init(sdkConfig.config.platformConf); } ZYSDK.ZYSDK.initSdk(sdkConfig.config.pointData.gid) let serverData = null; let uid: any = "123456"; while (serverData == null) { gg.ui.showToast("获取游戏存档..."); uid = await ZYSDK.ZYSDK.getUserId(); console.log("whileUid", uid); if (uid == null || uid == 1 || uid == "") await ZYSDK.ZYSDK.initSdk(sdkConfig.config.pointData.gid); serverData = await this.getServerData(); } let fun = () => { let str = serverData; if (serverData.length > 350) { str = serverData.substring(0, 350); } gg.sdk.reportDY("inLevel", `存档异常-${str}`); } if (serverData == "") { gg.sdk.reportDY("inLevel", `新玩家-${uid}`); } else if (serverData == undefined) { gg.sdk.reportDY("inLevel", `存档异常-undefined`); } else { try { let sd = JSON.parse(serverData); if (!Object.hasOwnProperty.call(sd, "uid") || !Object.hasOwnProperty.call(sd, "chapter")) { fun(); } } catch (error) { fun(); } } let v = ZYSDK.ZYSDK.getCustomValue(); if (v == 0) this.upTestData = false; else if (v == 100) this.upTestData = true; else { this.upTestData = Math.random() <= v / 100; } let lvs = await ZYSDK.ZYSDK.getLevel(); if (lvs && lvs.length > 0) { this.levelS = lvs; } // this.pf.login({ // force: true, // success(res) { // console.log(`login 调用成功${res.code} ${res.anonymousCode}`); // }, // fail(res) { // console.log(`login 调用失败`); // }, // }); if (this.timer) { clearInterval(this.timer); } if (sdkConfig.serverTimeCheckInterval > 0) { await this.updateServerTime(); this.timer = setInterval(() => { this.updateServerTime(); }, sdkConfig.serverTimeCheckInterval); } this.checkTime((curentTime) => { if (curentTime) { console.error("时间异常"); this.pfLogKey('SYS:TIME_INVALID', { local: curentTime }); gg.ui.showToast("时间异常,请检查时间设置"); gg.sdk.reportDY("inLevel", `时间异常-本地时间${curentTime}`); } }); this.onMemoryWarning((warnRes?: { level?: number }) => { const level = warnRes?.level; if (level != null) { this._lastMemWarnLevel = level; this._lastMemWarnMs = Date.now(); } if (level != null && level < 10) return; // 不主动 triggerGC;改升战斗特效/音效节流档位,减轻内存与卡帧 if (level != null && level >= 10) { BattlePerformance.onMemoryPressure(level); } try { const info = this.pf.getSystemInfoSync(); const dev = [info.brand, info.model].filter(Boolean).join(' ') || 'unknown'; let detail = level != null ? `lv=${level}|dev=${dev}` : `dev=${dev}`; if (MiniGamePlatform.isHarmonyOS) { detail += '|harmony=1'; } detail += '|gc=0'; if (PfDiagnostics.write('MEM', 'WARN', detail, 600000, 'warn')) { gg.sdk.reportDY('inLevel', `MEM-warn-${detail}`); } } catch { /* ignore */ } }); return serverData } checkTime(callback: Function = null) { if (!gg.sdk.pf || DEV || DEBUG || sys.isBrowser) { if (callback) { callback(); } return; } ZYSDK.ZYSDK.getTimestamp((res) => { if (res && res.ts) { let curentTime = new Date().getTime(); let servertime = res.ts * 1000; console.log("servertime:", servertime); console.log("curentTime:", curentTime); if (Math.abs(servertime - curentTime) > 1000 * 60 * 30) { callback(curentTime); } else { if (callback) { callback(); } } } }, () => { console.error("获取时间失败"); this.pfLogKey('SYS:TIME_FETCH_FAIL'); }); } /**获取服务器时间 */ getServerTime() { // console.log("本机时间毫秒数(UTC):", new Date().getTime()); // console.log("本机时间格式化(UTC):", new Date().toUTCString()); // console.log("本机时间格式化(本地):", new Date().toLocaleString()); // console.log("服务器时间毫秒数:", this.serverTime); // console.log("服务器时间格式化(UTC):", new Date(this.serverTime).toUTCString()); // console.log("服务器时间格式化(本地):", new Date(this.serverTime).toLocaleString()); if (DEV || DEBUG || sys.isBrowser || sdkConfig.serverTimeCheckInterval == 0) { let time = new Date().getTime(); //console.log("走了本地时间:", time, "serverTimeCheckInterval:", sdkConfig.serverTimeCheckInterval); return time; } return this.serverTime; } /**更新服务器时间 */ updateServerTime() { return new Promise((resolve, reject) => { if (!sdkConfig.serverTimeCheckInterval) { this.serverTime = Date.now(); resolve(null); return; } ZYSDK.ZYSDK.getTimestamp((res) => { if (res && res.ts) { let num = res.ts * 1000; this.serverTime = num; resolve(null); } }, () => { this.pfLogKey('SYS:SERVER_TIME_FAIL'); resolve(null); }); }); } initUpdate() { const updateManager = this.pf.getUpdateManager(); if (!updateManager) return; if (updateManager.onUpdateReady) { updateManager.onUpdateReady((res) => { this.pf.showModal({ title: "更新提示", content: "新版本已经准备好,是否重启小游戏?", success: (res) => { if (res.confirm) { // 新的版本已经下载好,调用 applyUpdate 应用新版本并重启 // assetManager.cacheManager.clearCache(); updateManager.applyUpdate(); } }, }); }); } if (updateManager.onUpdateFailed) { updateManager.onUpdateFailed((err) => { // 新的版本下载失败 console.log("版本下载失败原因", err); this.pfLogKey('SYS:UPDATE_FAIL', err); this.pf.showToast({ title: "新版本下载失败,请稍后再试", icon: "none", }); }); } if (updateManager.onCheckForUpdate) { updateManager.onCheckForUpdate(); } } restartMiniProgramSync() { if (this.pf && this.pf.restartMiniProgramSync) { this.pf.restartMiniProgramSync(); } } getServerData() { if (EDITOR || DEBUG || sys.platform == sys.Platform.DESKTOP_BROWSER || sys.platform == sys.Platform.MOBILE_BROWSER) { return new Promise((resolve, reject) => { resolve(""); }) } return new Promise((resolve, reject) => { ZYSDK.ZYSDK.getGameData((data) => { resolve(data); }, async () => { resolve(null); }) }) } /** * 当前平台进入前台 * @param res */ onPfShow(res) { console.log("onPfShow", res); this.query = res.query; this._show_time = new Date().getTime(); gg.game?.CurentBattle?.TetraMap?.stabilizeAfterViewportChange?.(); //this._restoreVideoAdRuntimeOnForeground(); if (this._onShare) { let t = new Date().getTime() - this._share_time; this._onShare(t); this._onShare = null; } // if (res.launch_from == 'homepage' && res.location == 'sidebar_card') { // //GEvent.Ins.emit(GEventType.CheckIsEnterFromCeBianLan, true); // gga.data.setStatus(StatusType.DayGiftCanGet, 1); // gga.data.saveToServer(); // } if ((res.launch_from == 'homepage' && res.location == 'sidebar_card') || (res.launch_from == 'sidebar_miniprogram' || res.launch_from == 'sidebar_new')) { //GEvent.Ins.emit(GEventType.CheckIsEnterFromCeBianLan, true); gg.data.setStatus(StatusType.DayGiftCanGet, 1); // gg.data.saveToServer(); } } private _show_time = 0; /** * 当前平台进入后台 */ onPfHide() { let c = new Date().getTime(); let t = c - this._show_time; t = Math.floor(t / 1000); console.log("onPfHide,在线时长:" + t); } /** * 抖音全屏激励视频:平台会挂起小游戏,禁止再 game.pause()(叠加后易黑屏)。 * 微信 overlay 广告:需 game.pause() 冻结主循环与物理。 */ private _shouldPauseEngineForVideoAd(): boolean { return MiniGamePlatform.shouldPauseEngineForVideoAd(); } /** * 激励视频播放前:挂起物理/音频/(微信)主循环;战斗由 SDK 统一 pause('videoAd')。 * 调用方不得在 showVideoAd 前自行 battle.pause,否则 leave 无法对称恢复战斗与物理。 */ private _enterVideoAdPause(): void { Physics2DGate.suspend(Physics2DGate.VideoAd); gg.audio.pause(); // if (this._shouldPauseEngineForVideoAd()) { // } game.pause(); this._videoAdBattlePausedBySdk = false; const battle = gg.game?.CurentBattle; if (battle && !battle.IsGameOver && !battle.IsGamePause) { battle.pause('videoAd'); this._videoAdBattlePausedBySdk = true; } } private _isVideoAdPaused = false; /** 激励视频 onClose:与 enter 对称恢复;仅恢复本链路 pause 的战斗。 */ private _leaveVideoAdPause(): void { // if (this._shouldPauseEngineForVideoAd()) { // } game.resume(); Physics2DGate.resume(Physics2DGate.VideoAd); gg.audio.resume(); if (this._videoAdBattlePausedBySdk) { gg.game?.CurentBattle?.resumeFromVideoAdSdk(); this._videoAdBattlePausedBySdk = false; } } /** * 全屏激励视频:从小程序广告页回到游戏画布时,onShow 往往早于 onClose。 * 须先恢复物理/音频/战斗,避免「弹道飞到怪身上却无碰撞」;onClose 仍负责 depth-- 与发奖。 */ private _restoreVideoAdRuntimeOnForeground(): void { if (this._videoAdDepth <= 0) return; if (!RewardedVideoAd.hasPendingCallback()) return; Physics2DGate.resume(Physics2DGate.VideoAd); if (this._shouldPauseEngineForVideoAd()) { game.resume(); } gg.audio.resume(); if (this._videoAdBattlePausedBySdk) { gg.game?.CurentBattle?.resumeFromVideoAdSdk(); } } /** * 显示视频广告 * @param callback 回调 */ showVideoAd(callback: Function) { console.log('显示视频广告'); if (this._isVideoAdPaused) return; if (gg.data.getItemNum(ItemNumType.skipAd) > 0) { gg.data.subItemNum(ItemNumType.skipAd, 1); callback(true, "skipAd"); return; } if (EDITOR || !this.showAd) { gg.data.addDayTaskProgressData(TaskType.videoAd, 1); callback(true, "editor"); return; } if (sys.isBrowser) { gg.data.addDayTaskProgressData(TaskType.videoAd, 1); callback(true, window.location.href); return; } this._isVideoAdPaused = true; void MiniGamePlatform.checkNetworkForRewardedVideo().then((online) => { if (!online) { this._isVideoAdPaused = false; gg.ui.showToast('网络请求失败,请稍后再试'); this.pfLogKey('AD:NETWORK_OFFLINE'); callback(false); return; } this._enterVideoAdPause(); RewardedVideoAd.show((res) => { try { this._isVideoAdPaused = false; this._leaveVideoAdPause(); if (res) { callback(true); gg.data.addStatistics(StatisticsType.TodayAdCount, 1); gg.data.addDayTaskProgressData(TaskType.videoAd, 1); ZYSDK.ZYSDK.reportVideo(true); } else { this.pfLogKey('AD:REWARD_USER_FAIL'); callback(false); ZYSDK.ZYSDK.reportVideo(false); } } catch (e) { console.error('[SDKManager] showVideoAd callback error', e); this.pfLogKey('AD:REWARD_CALLBACK_ERR', e); } }); }); } /** * 上次显示插屏广告时间戳 */ private _interAdTimeStamp = 0; /** * 显示插屏广告 * @param onAdClose * @param onFail */ showInterstitialAd(onAdClose?: Function, onFail?: Function, useAdLevel = false) { if (EDITOR) { onAdClose && onAdClose(true, "editor"); } else if (sys.isBrowser) { onAdClose && onAdClose(true, window.location.href); } else { if (!useAdLevel || ZYSDK.ZYSDK.getAdLevel() == 1) { //判断是否间隔x秒 let curTime = new Date().getTime(); if (curTime - this._interAdTimeStamp < ZYSDK.ZYSDK.getAdInterval() * 1000) { onFail && onFail(); return; } this._interAdTimeStamp = curTime; InterstitialAd.showInterstitialAd(onAdClose, onFail, () => { this.reportDY("inLevel", `插屏-章节${gg.data.doc.chapter}`); }); } } } /** * 设置排行榜 * @param value 分数 * @param dataType 数据类型 0 数字类型 1 字符串类型 * @param priority 权重 * @param zoneId 排行榜分区 */ setImRankData(value: number | string, dataType = 0, priority = 0, zoneId = "default") { if (EDITOR) { console.log('平台无法写入排行榜数据') } else if (sys.isBrowser) { console.log('平台无法写入排行榜数据') } else { TTRank.setImRankData(value, dataType, priority, zoneId) } } /** * 获取排行榜 * @param rankTitle 排行榜标题 * @param suffix 分数后缀补充文案 为空或不填,一般枚举类型不需要填后缀 * @param rankType 排行榜类型 可选值有:day、week、month、month、all * @param zoneId 排行榜分区 * @param dataType 数据类型: 0 数字类型 1 字符串类型 * @param relationType 排行榜类型 可选值有:default 好友榜、总榜都展示 friend 仅展示好友榜 all 仅展示总榜 */ getImRankList(rankTitle: string = "排行榜", suffix = "", rankType = "all", zoneId = "default", dataType = 0, relationType = "default") { if (EDITOR) { console.log('平台无法获取排行榜数据') } else if (sys.isBrowser) { console.log('平台无法获取排行榜数据') } else { TTRank.getImRankList(rankTitle, suffix, rankType, zoneId, dataType, relationType) } } /** * 打点 * @param event * @param msg */ reportDY(event, msg) { //添加打点打印和格式,带颜色 //console.log("%c打点==", "color:green", msg); if (sys.platform == sys.Platform.BYTEDANCE_MINI_GAME) { // window["tt"].reportAnalytics(event, { // type: msg, // }); ZYSDK.ZYSDK.reportUserAction(msg); GEMgr.GEReportEvent(msg); } if (sys.platform == sys.Platform.WECHAT_GAME) { //event = event.toLowerCase(); // window["wx"].reportEvent(event, { // "type": msg, // }); ZYSDK.ZYSDK.reportUserAction(msg); GEMgr.GEReportEvent(msg); } } /** * 进入抖音侧边栏 * @param callback */ enterSidebar(callback?: Function) { if (window["tt"] && window["tt"].navigateToScene) { window["tt"].navigateToScene({ scene: "sidebar", success: (res) => { console.log("navigate to scene success"); if (callback) callback(true); }, fail: (res) => { console.log("navigate to scene fail: ", res); if (callback) callback(false); }, }) } else if (window["ks"] && window["ks"].navigateToScene) { window["ks"].navigateToScene({ scene: "sidebar", success: (res) => { console.log("navigate to scene success"); if (callback) callback(true); }, fail: (res) => { console.log("navigate to scene fail: ", res); if (callback) callback(false); }, }) } else { if (callback) callback(true); } } /**验证是否支持侧边栏 */ checkSidebar(callback?: Function) { if (window["tt"] && window["tt"].navigateToScene) { if (callback) callback(true); } else if (window["ks"]) { if (window["ks"].checkSliderBarIsAvailable) { window["ks"].checkSliderBarIsAvailable({ success: (result) => { console.log("check slider bar is available success: ", result.available); if (result && result.available) { if (callback) callback(true); } else { if (callback) callback(false); } }, fail: (res) => { console.log("check slider bar is available fail: ", res); if (callback) callback(false); }, }) } else { if (callback) callback(false); } } else { if (callback) callback(false); } // if (!window["tt"]) { // if (callback) callback(false); // return; // } else { // window["tt"].checkScene({ // scene: "sidebar", // success: (res) => { // console.log("check scene success: ", res.isExist); // //成功回调逻辑 // if (callback) callback(true); // }, // fail: (res) => { // console.log("check scene fail:", res); // //失败回调逻辑 // if (callback) callback(false); // } // }); // } } /** * 验证是否需要显示添加到桌面按钮 * @param callback */ checkNeedShowAddShortcut(callback?: Function) { if (!window["tt"]) callback?.(false); else if (!window["tt"].addShortcut) callback?.(false); else callback?.(true); } /** * 分享游戏 * @param timeLimit 分享最小停留时间, 没有回调的平台通过计时判断是否分享成功 * @param cb 有回调的平台,通过回调函数判断是否分享成功 * @param data 可选参数,分享数据(需要知道参数类型) */ share(timeLimit, cb = null, data = null) { this._share_time = new Date().getTime(); if (this.Share) { let desc = "" let url = "" let query = "" let id = ""; if (data && data.desc) desc = data.desc; if (data && data.res) url = data.res; if (data && data.query) query = data.query; if (data && data.shareid) id = data.shareid; if (sys.platform == sys.Platform.WECHAT_GAME || MiniGamePlatform.kind === MiniGameKind.Kuaishou || MiniGamePlatform.kind === MiniGameKind.Bilibili) { this._onShare = (time) => { console.log("本次分享时间:" + time); if (time >= timeLimit) { if (cb) cb(true); } else { if (cb) cb(false); } } this.Share.share(desc, url, cb, query); } else if (sys.platform == sys.Platform.BYTEDANCE_MINI_GAME) { this.Share.share(desc, url, cb, query, id); } else { cb(true); } } else { cb(true); } } private _share_time = 0; private _onShare = null; /** * 短时间震动 */ vibrateShort() { this.Vibrate.vibrateShort(); } /** * 长时间震动 */ vibrateLong() { this.Vibrate.vibrateLong(); } /** * 开始震动 */ startVibrate() { this.Vibrate.startVibrate(); } /** * 停止震动 */ stopVibrate() { this.Vibrate.stopVibrate(); } /** * 添加到桌面 * @param callback 回调 */ addShortcut(callback?: Function) { if (window["tt"] && window["tt"].addShortcut) { window["tt"].addShortcut({ success: function (res) { console.log("添加成功"); if (callback) callback(true); }, fail: function (res) { console.log("添加失败", res); if (callback) callback(false); } }) } else if (window["ks"] && window["ks"].addShortcut) { window["ks"].addShortcut({ success: function (res) { console.log("添加成功"); if (callback) callback(true); }, fail: function (res) { console.log("添加失败"); if (callback) callback(false); } }) } else { if (callback) callback(false); } } getVideoPath() { return this.Recording?.getVideoPath(); } getVideoTime() { return this.Recording?.getVideoTime(); } getStatus() { return this.Recording?.getStatus(); } /** * 开始录屏 */ startRecord() { this.Recording && this.Recording.startVideo(); } /** * 停止录屏 */ stopRecord() { this.Recording && this.Recording.stopVideo(); } /** * 分享录屏 */ shareRecord(callback = null) { if (this.Recording) this.Recording.shareVideo(callback); else callback(true); } pauseVideo() { this.Recording && this.Recording.pauseVideo(); } resumeVideo() { this.Recording && this.Recording.resumeVideo(); } /** * 微信转发菜单 */ openWXShareMenu() { if (WECHAT) { window["wx"].showShareMenu({ withShareTicket: true, menus: ['shareAppMessage', 'shareTimeline'], success: () => { }, fail: () => { }, complete: () => { } }); window["wx"].onShareAppMessage(function () { let share1 = { title: sdkConfig.config.platformConf.wxOnShareMessage.title, imageUrlId: sdkConfig.config.platformConf.wxOnShareMessage.imageUrlId, imageUrl: sdkConfig.config.platformConf.wxOnShareMessage.imageUrl } // let share2 = { // title: "看不见下雨了嘛,赶紧回笼呀!", // imageUrlId: 'Y2PHkx0bR0Ou2a25kg+Vsg==', // imageUrl: 'https://mmocgame.qpic.cn/wechatgame/WFTmogtfYrIwyhRVuZHbEs709mroiaaDcYkwBSBXLckR7ibQNPfrXKstvCpyV4bmyn/0' // } // let share3 = { // title: "快救救这只猪", // imageUrlId: 'qTQmv6/8SCixA6raayq2HA==', // imageUrl: 'https://mmocgame.qpic.cn/wechatgame/qqRAxsORaczSj5Z33DB4pneEtpOBstVwzhsk8VPh02OlTGHzPHBxicaffB09ILdgl/0' // } // return Math.random() > 0.5 ? share1 : Math.random() > 0.5 ? share2 : share3; return share1 }); if (gg.data.getStatistics(StatisticsType.yonghuxieyiYS) == 0) { window["wx"].getPrivacySetting({ success: (res) => { console.log("是否需要授权:", res.needAuthorization); console.log("隐私协议名称:", res.privacyContractName); if (res.needAuthorization) { window["wx"].requirePrivacyAuthorize({ success: () => { // 用户同意授权 // runGame() 继续游戏逻辑 console.log("用户同意授权"); gg.data.setStatistics(StatisticsType.yonghuxieyiYS, 2); }, fail: () => { console.log("用户拒绝授权"); gg.data.setStatistics(StatisticsType.yonghuxieyiYS, 1); }, // 用户拒绝授权 complete: () => { } }) } else { } }, fail: () => { // 低版本基础库不支持时的处理 } }); } } } getUserInfo() { return new Promise((resolve, reject) => { if (this._userInfo) { resolve(this._userInfo); return; } if (!this.pf || !this.pf["getUserInfo"]) { resolve(null); return; } this.pf["getUserInfo"]({ // withCredentials: true, // withRealNameAuthenticationInfo: true, success(res) { console.log(`getUserInfo 调用成功`, res.userInfo, res.encryptedData, res.iv, res.signature); this._userInfo = res.userInfo; resolve(res.userInfo); }, fail(res) { console.log(`getUserInfo 调用失败`, res.errMsg); resolve(null); }, }); }); } private _userInfo: any = null; //推荐流订阅 PlatformFeedSubscribe() { console.log('PlatformFeedSubscribe') let self = this if (this.pf && window["tt"]) { console.log('PlatformFeedSubscribe1') let sceneId = 1 //离线收益场景 let self = this if (this.pf.canIUse("checkFeedSubscribeStatus")) { console.log('PlatformFeedSubscribe2') this.pf.checkFeedSubscribeStatus({ type: "play", scene: 1, success(res) { console.log("checkFeedSubscribeStatus:" + res.status) if (!res.status) { // 用户没订阅 let contentIDs = sdkConfig.config.platformConf.contentIDs || []; console.log("contentIDs:", contentIDs) self.pf.requestFeedSubscribe({ type: "play", scene: sceneId, contentIDs: contentIDs, success(res) { console.log("requestFeedSubscribe:" + res.success) }, fail(res) { console.log(res.errMsg) }, }) } }, fail(res) { console.log(res.errMsg) }, }) } } } //推荐流上报场景 PlatformReportScene() { if (this.pf && this.pf.reportScene) { this.pf.reportScene({ sceneId: 7001, costTime: 2000, success(res) { // 上报接口执行完成后的回调,用于检查上报数据是否符合预期 console.log("ReportScene_success:" + res) }, fail(res) { // 上报报错时的回调,用于查看上报错误的原因:如参数类型错误等 console.log("ReportScene_fail:" + res) } }) } } getLaunchOptionsSync_custom() { if (this.pf && this.pf.getLaunchOptionsSync) { let obj = this.pf.getLaunchOptionsSync(); let str = JSON.stringify(obj); console.log("getLaunchOptionsSync_custom: 场景值", str) ///obj.scene return obj } else { return null } } private _bindGlobalErrorReporter(): void { try { const pf = this.pf as MiniGameHostApi & { onError?: (cb: (err: unknown) => void) => void; onUnhandledRejection?: (cb: (res: { reason?: unknown }) => void) => void; }; pf?.onError?.((err) => { this.pfLogKey('RUNTIME:UNCAUGHT', err, 300000); }); pf?.onUnhandledRejection?.((res) => { this.pfLogKey('RUNTIME:PROMISE_REJECT', res?.reason, 300000); }); } catch { /* ignore */ } } /** 同 tag 平台日志冷却(毫秒),避免刷爆配额 */ private _pfLogKeyLastMs: Record = {}; /**平台记录日志 */ pfLog(msg: string) { if (this.pf && this.pf.getLogManager) { this.pf.getLogManager().log(msg); } else { console.log(msg); } } /** * 关键行为 / 失败 / 异常 → 平台日志(getLogManager + RealtimeLogManager)。 * @param tag 短标签,如 RES:PREFAB_FAIL、AD:REWARD_SHOW_FAIL * @param detail 附加信息(字符串、Error 或可 JSON 序列化对象) * @param cooldownMs 同 tag 冷却,默认 60s */ pfLogKey(tag: string, detail?: unknown, cooldownMs = 60000): void { if (!tag) return; const now = Date.now(); const last = this._pfLogKeyLastMs[tag] ?? 0; if (now - last < cooldownMs) return; this._pfLogKeyLastMs[tag] = now; const ctx = this._formatPfLogContext(); const detailStr = this._stringifyPfDetail(detail); let msg = detailStr ? `[${tag}] ${ctx} ${detailStr}` : `[${tag}] ${ctx}`; if (msg.length > 250) msg = msg.slice(0, 250); this.pfLog(msg); try { const pfAny = this.pf as { getRealtimeLogManager?: (opts?: { syncToConsole?: boolean }) => { error?: (m: string) => void } } | null; const getRt = pfAny?.getRealtimeLogManager; if (typeof getRt === 'function') { getRt.call(pfAny, { syncToConsole: !!DEV })?.error?.(msg); } } catch { /* ignore */ } if (DEV) { console.warn('[pfLogKey]', msg); } } private _formatPfLogContext(): string { const b = gg.game?.CurentBattle; const ch = b?.getReportDYChapterId?.() ?? gg.data?.doc?.chapter ?? 0; const wave = b?.CurentWaveNum ?? 0; return `ch${ch}_w${wave}`; } private _stringifyPfDetail(detail: unknown): string { if (detail == null) return ''; if (typeof detail === 'string') return detail; if (detail instanceof Error) return `${detail.name}:${detail.message}`; try { const s = JSON.stringify(detail); return s.length > 140 ? s.slice(0, 140) : s; } catch { return String(detail); } } /**平台记录调试日志 */ pfDebug(msg: string) { if (this.pf && this.pf.getLogManager) { this.pf.getLogManager().debug(msg); } else { console.debug(msg); } } ttWorker(callback) { // if (this.pf && this.pf.createWorker) { // let worker = this.pf.createWorker({ // scriptPath: "worker.js", // success: (res) => { // console.log("createWorker success", res); // callback(res); // }, // fail: (res) => { // console.log("createWorker fail", res); // }, // }); // } } /** 界面打开失败等场景:附带最近一次内存告警等级与距今秒数 */ getMemWarnState(): { level: number | null; agoSec: number | null } { if (this._lastMemWarnLevel == null) { return { level: null, agoSec: null }; } return { level: this._lastMemWarnLevel, agoSec: Math.floor((Date.now() - this._lastMemWarnMs) / 1000), }; } formatMemWarnForPf(): string { const s = this.getMemWarnState(); if (s.level == null) { return 'memLv=na'; } return `memLv=${s.level}|memAgo=${s.agoSec}s`; } formatMemWarnForDy(): string { const s = this.getMemWarnState(); if (s.level == null) { return 'memna'; } return `mem${s.level}_ago${s.agoSec}s`; } /** * 平台 triggerGC(已禁用)。 * 线上数据 8/21 卡顿率 11%→14% 与进/离局 force GC 高度相关;改由 releaseBundle + 内存压力节流。 */ pfTriggerGC(_force = false): boolean { if (!SDKManager.PF_GC_ENABLED) { return false; } if (!this.pf?.triggerGC) { return false; } const now = Date.now(); if (!_force && now - this._lastPfGcMs < SDKManager.PF_GC_COOLDOWN_MS) { return false; } this._lastPfGcMs = now; try { this.pf.triggerGC(); return true; } catch { return false; } } /** 平台内存警告(callback 参数含 level,Android/鸿蒙等) */ onMemoryWarning(callback: ((res?: { level?: number }) => void) | null = null) { if (this.pf && this.pf.onMemoryWarning) { this.pf.onMemoryWarning((res?: { level?: number }) => { callback?.(res); }); } } }