From 2a599b0356f2d307271ed347648e29cba29e20fb Mon Sep 17 00:00:00 2001 From: richarjiang Date: Tue, 19 May 2026 22:56:31 +0800 Subject: [PATCH] =?UTF-8?q?fix=EF=BC=9A=20=E4=BF=AE=E5=A4=8D=E4=B8=80?= =?UTF-8?q?=E7=B3=BB=E5=88=97=20bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .DS_Store | Bin 8196 -> 10244 bytes assets/PageLoading.ts | 18 + assets/main.ts | 6 + assets/prefabs/PageHome.prefab | 2 +- assets/prefabs/PageHome.ts | 18 +- assets/prefabs/PageLevel.ts | 57 +- assets/prefabs/PagePKEnd.prefab | 42 +- assets/prefabs/PassModal.prefab | 2 +- assets/prefabs/PassModal.ts | 21 +- .../scripts/utils/AchievementTitleManager.ts | 2 +- assets/scripts/utils/ShareLaunchHandler.ts | 147 +++ .../scripts/utils/ShareLaunchHandler.ts.meta | 9 + assets/scripts/utils/WxSDK.ts | 73 +- settings/mcp-server.json | 6 + settings/tool-manager.json | 957 ++++++++++++++++++ 15 files changed, 1321 insertions(+), 39 deletions(-) create mode 100644 assets/scripts/utils/ShareLaunchHandler.ts create mode 100644 assets/scripts/utils/ShareLaunchHandler.ts.meta create mode 100644 settings/mcp-server.json create mode 100644 settings/tool-manager.json diff --git a/.DS_Store b/.DS_Store index 88927d384a0b937262bc6f6a5a85522e0e7f0253..ead13241b74ad596b4c488f565ca430f79a089d8 100644 GIT binary patch delta 887 zcmZp1XbF&DU|?W$DortDU{C-uIe-{M3-C-V6q~50$jH4hU^hP__hue}2)24IhE#?M zh7ur|$570W$&e4Clgf(=l5+BsfVy@h735?VmlzmaV`O4xVP#|IVCUfGhz-ujFApwB zEGaE^N-T;7@j~+RbCO`}#H6sy)be-%5$F88lElos)FQBk%#>81l9=$!yp;TMr~J~q zl=@<@=3s~n2PX$-ynsY?wWX1{j)IwyVXck=kZqu&U}9`mTg%BIs;qAv6rY`wo0s1O zbQ};cGD2tuUMLNtx`7O=p36x$3{K9^EdVQ&geb?NaI=&^CKFS-!sL8W%gOtM4hUmY z(<%If5u)ZM_K?iYcX5G+B!}%Qk+QIR#~pDwEH^)eXtf0yNS*-uK?%EW%YuvYa`N-i zfod2x7H(w}WQO=yfg4D>f>OoC!tczJ`DFq{m>_9I1Ec^Lssa)C_mW=7FuM(o}j MO(+a`lcfYY0cVra>;M1& delta 102 zcmZn(XmOBWU|?W$DortDU;r^WfEYvza8E20o2aMA$h { + this.node.destroy(); + }, + }); + return; + } + ViewManager.instance.open('PageHome', { onComplete: () => { this.node.destroy(); diff --git a/assets/main.ts b/assets/main.ts index d818b1d..35ced4c 100644 --- a/assets/main.ts +++ b/assets/main.ts @@ -2,6 +2,7 @@ import { _decorator, Component, Prefab, AudioClip } from 'cc'; import { ViewManager } from './scripts/core/ViewManager'; import { ToastManager } from './scripts/utils/ToastManager'; import { AudioManager } from './scripts/utils/AudioManager'; +import { ShareLaunchHandler } from './scripts/utils/ShareLaunchHandler'; const { ccclass, property } = _decorator; /** @@ -115,5 +116,10 @@ export class main extends Component { } AudioManager.instance.init(this.buttonClickAudio, this.node); + + // 注册 wx.onShow / wx.onHide: + // 用户把小游戏退到后台后再点击好友分享卡片,能拿到最新的 shareCode 并直达分享挑战关卡。 + // 必须在 PageLoading 跑之前注册,这样初始 launch 中的 shareCode 也会被作为种子记下。 + ShareLaunchHandler.instance.init(); } } diff --git a/assets/prefabs/PageHome.prefab b/assets/prefabs/PageHome.prefab index 097993e..827a972 100644 --- a/assets/prefabs/PageHome.prefab +++ b/assets/prefabs/PageHome.prefab @@ -5507,7 +5507,7 @@ "b": 0, "a": 255 }, - "_string": "还差3题获得冷场小白2级", + "_string": "还差3题,解锁新成就等级", "_horizontalAlign": 1, "_verticalAlign": 1, "_actualFontSize": 40, diff --git a/assets/prefabs/PageHome.ts b/assets/prefabs/PageHome.ts index ddb5c8f..c86adb9 100644 --- a/assets/prefabs/PageHome.ts +++ b/assets/prefabs/PageHome.ts @@ -61,7 +61,7 @@ export class PageHome extends BaseView { /** 是否正在播放体力消耗动画 */ private _isAnimating: boolean = false; - /** 进度游标 0% 时的本地 X 坐标,使用 prefab 当前摆放位置作为起点 */ + /** 进度游标 0% 时的本地 X 坐标,根据 ProgressBar Bar 子节点的左端推导出来 */ private _progressAnchorStartX: number | null = null; /** @@ -363,11 +363,23 @@ export class PageHome extends BaseView { } private _cacheProgressAnchorStartX(): void { - if (this._progressAnchorStartX !== null || !this.progressAnchor) { + if (this._progressAnchorStartX !== null || !this.titleProgressBar) { return; } - this._progressAnchorStartX = this.progressAnchor.position.x; + const barSprite = this.titleProgressBar.barSprite; + if (!barSprite) { + return; + } + + // Bar 节点 anchor 为 (0, 0.5),其本地 position.x 即为进度条可视左端。 + // ProgressBar 与 ProgressAnchor 共享同一父节点(TitleLevel), + // 因此把 Bar 的本地 X 按 ProgressBar 自身的位移与缩放映射到父节点空间, + // 才是真正的「0% 起点」。直接拿 progressAnchor.position.x 当起点会导致 + // 气泡始终被 prefab 摆放偏移量带跑(实测偏右 ~24px)。 + const progressBarNode = this.titleProgressBar.node; + const barLocalX = barSprite.node.position.x; + this._progressAnchorStartX = progressBarNode.position.x + barLocalX * progressBarNode.scale.x - 30; } private _updateProgressAnchor(progress: number): void { diff --git a/assets/prefabs/PageLevel.ts b/assets/prefabs/PageLevel.ts index 4c03748..fcaff9e 100644 --- a/assets/prefabs/PageLevel.ts +++ b/assets/prefabs/PageLevel.ts @@ -292,6 +292,14 @@ export class PageLevel extends BaseView { /** 是否处于分享挑战模式 */ private _isShareMode: boolean = false; + /** + * 当前 PageLevel 实例所处分享挑战的 shareCode 缓存。 + * PageLevel 注册时 cache: true,复用同一个实例。 + * 当用户在后台切换好友分享卡片时,ShareManager.shareCode 会发生变化, + * onViewShow 通过对比这个值与最新的 ShareManager.shareCode 来判断是否需要 _reinitLevelSession。 + */ + private _activeShareCode: string | null = null; + /** 体力恢复倒计时定时器 */ private _staminaTimerId: ReturnType | null = null; @@ -335,8 +343,10 @@ export class PageLevel extends BaseView { this._shareSubmissions.clear(); this._isSubmittingShareResult = false; this._hasRequestedShareUserInfo = false; + this._activeShareCode = ShareManager.instance.shareCode; console.log('[PageLevel] 进入分享挑战模式'); } else { + this._activeShareCode = null; // 从 AuthManager 获取首关数据(由 PageLoading → game-data 提供) const nextLevel = AuthManager.instance.nextLevel; if (nextLevel) { @@ -373,12 +383,41 @@ export class PageLevel extends BaseView { const params = this.getParams(); const desiredShareMode = params?.shareMode === true; - if (desiredShareMode !== this._isShareMode) { - console.log(`[PageLevel] 检测到模式切换 ${this._isShareMode} → ${desiredShareMode},重新初始化关卡会话`); + // 当前 ShareManager 中的 shareCode(可能因为后台切到新的分享卡片而变化) + const latestShareCode = ShareManager.instance.shareCode; + + const modeChanged = desiredShareMode !== this._isShareMode; + // 同样是分享模式,但 ShareManager 中的 shareCode 已经换了一份题单 —— 也必须重建会话 + const shareCodeChanged = desiredShareMode && latestShareCode !== this._activeShareCode; + + if (modeChanged || shareCodeChanged) { + console.log( + `[PageLevel] 检测到模式/分享码切换 mode:${this._isShareMode}->${desiredShareMode} ` + + `code:${this._activeShareCode}->${latestShareCode},重新初始化关卡会话`, + ); this._reinitLevelSession(desiredShareMode); return; } + // 上一次离场时如果停留在「答对后通关流程」(_isTransitioning=true 由 showSuccess 置位、 + // 而 _applyLevelConfig 才会重置),缓存的 PageLevel 实例会保留完成态: + // - 输入格已填入正确答案 + // - 提交按钮被 _isTransitioning 锁住,无法重新提交 + // - 倒计时已停、谐音梗已揭示 + // 此时玩家从首页再次进入会看到一个无法操作的"死局"。必须把会话推进到下一关。 + // 注意:这只可能在主线模式发生 —— 分享模式下点 iconSetting / PassModal 的 home + // 都会调用 ShareManager.clearShareMode + ViewManager.replace,再次进入会被 + // 上面的 modeChanged / shareCodeChanged 分支拦截走 _reinitLevelSession。 + if (this._isTransitioning) { + console.log('[PageLevel] 上次离场时停留在通关后状态,自动推进到下一关'); + this._closePassModal(); + this._closeWrongModal(); + this._closeTimeoutModal(); + this._closeCommonModal(); + void this.goToNextLevel(); + return; + } + this._refreshModeUI(); this.updateStaminaLabel(); if (!this._isShareMode) { @@ -408,8 +447,10 @@ export class PageLevel extends BaseView { this._hasRequestedShareUserInfo = false; if (this._isShareMode) { - console.log('[PageLevel] 切换到分享挑战模式'); + this._activeShareCode = ShareManager.instance.shareCode; + console.log(`[PageLevel] 切换到分享挑战模式 (shareCode=${this._activeShareCode})`); } else { + this._activeShareCode = null; // 主线模式:从 AuthManager 拉取最新的 nextLevel this._nextLevelData = null; const nextLevel = AuthManager.instance.nextLevel; @@ -866,6 +907,16 @@ export class PageLevel extends BaseView { console.log('[PageLevel] IconSetting 点击,返回主页'); AudioManager.instance.playButtonClick(); + // 离开 PageLevel 时把所有挂在 Canvas 上的关卡级弹窗一起清掉。 + // PassModal / WrongModal / TimeoutModal / CommonModal 都是 addChild 到 this.node.parent + // 也就是 PageLevel 的兄弟节点,PageLevel 被 ViewManager 隐藏后它们并不会自动消失, + // 否则会孤儿地盖在 PageHome 上。同时清掉 PassModal 也避免再次进入时缓存实例 + // 残留 _passModalNode 引用让 _swapToNextLevelImagesIfReady 误判弹窗仍在打开。 + this._closePassModal(); + this._closeWrongModal(); + this._closeTimeoutModal(); + this._closeCommonModal(); + // 分享模式下栈中没有 PageHome,需要清除分享状态并直接打开首页 if (this._isShareMode) { ShareManager.instance.clearShareMode(); diff --git a/assets/prefabs/PagePKEnd.prefab b/assets/prefabs/PagePKEnd.prefab index a490cd4..508c2c9 100644 --- a/assets/prefabs/PagePKEnd.prefab +++ b/assets/prefabs/PagePKEnd.prefab @@ -244,7 +244,7 @@ "__id__": 1 }, "_children": [], - "_active": true, + "_active": false, "_components": [ { "__id__": 11 @@ -426,7 +426,7 @@ "__id__": 25 } ], - "_active": true, + "_active": false, "_components": [ { "__id__": 71 @@ -2106,8 +2106,8 @@ }, "_lpos": { "__type__": "cc.Vec3", - "x": -172.179, - "y": -613.418, + "x": -142.654, + "y": 760.825, "z": 0 }, "_lrot": { @@ -2147,7 +2147,7 @@ }, "_contentSize": { "__type__": "cc.Size", - "width": 495, + "width": 660, "height": 75.6 }, "_anchorPoint": { @@ -2186,8 +2186,8 @@ "_string": "揭晓以下谐音梗的答案吧", "_horizontalAlign": 1, "_verticalAlign": 1, - "_actualFontSize": 45, - "_fontSize": 45, + "_actualFontSize": 60, + "_fontSize": 60, "_fontFamily": "Arial", "_lineHeight": 60, "_overflow": 0, @@ -2241,8 +2241,6 @@ "__id__": 0 }, "fileId": "90w8HRdbBPLYFqBkhPhWfM", - "instance": null, - "targetOverrides": null, "nestedPrefabInstanceRoots": null }, { @@ -2276,7 +2274,7 @@ "_lpos": { "__type__": "cc.Vec3", "x": -8.201, - "y": -852.319, + "y": 492.399, "z": 0 }, "_lrot": { @@ -2333,7 +2331,7 @@ "_lpos": { "__type__": "cc.Vec3", "x": 0, - "y": 0, + "y": -80.522, "z": 0 }, "_lrot": { @@ -2345,9 +2343,9 @@ }, "_lscale": { "__type__": "cc.Vec3", - "x": 1, - "y": 1, - "z": 1 + "x": 1.363, + "y": 1.363, + "z": 1.363 }, "_mobility": 0, "_layer": 1073741824, @@ -2494,7 +2492,7 @@ }, "_lpos": { "__type__": "cc.Vec3", - "x": -248.28, + "x": -202.789, "y": -12.833, "z": 0 }, @@ -2507,9 +2505,9 @@ }, "_lscale": { "__type__": "cc.Vec3", - "x": 0.537, - "y": 0.537, - "z": 0.767 + "x": 0.678, + "y": 0.678, + "z": 0.968 }, "_mobility": 0, "_layer": 1073741824, @@ -2789,9 +2787,9 @@ }, "_lscale": { "__type__": "cc.Vec3", - "x": 0.488, - "y": 0.488, - "z": 0.488 + "x": 0.422, + "y": 0.422, + "z": 0.422 }, "_mobility": 0, "_layer": 1073741824, @@ -3839,8 +3837,6 @@ "__id__": 0 }, "fileId": "bdqvu61fVFTIU1TQqs/qES", - "instance": null, - "targetOverrides": null, "nestedPrefabInstanceRoots": null }, { diff --git a/assets/prefabs/PassModal.prefab b/assets/prefabs/PassModal.prefab index c144097..0e514ec 100644 --- a/assets/prefabs/PassModal.prefab +++ b/assets/prefabs/PassModal.prefab @@ -1668,7 +1668,7 @@ "b": 0, "a": 255 }, - "_string": "还差3题获得冷场小白2级", + "_string": "还差3题,解锁新成就等级", "_horizontalAlign": 1, "_verticalAlign": 1, "_actualFontSize": 40, diff --git a/assets/prefabs/PassModal.ts b/assets/prefabs/PassModal.ts index c2e77f7..879801f 100644 --- a/assets/prefabs/PassModal.ts +++ b/assets/prefabs/PassModal.ts @@ -93,7 +93,7 @@ export class PassModal extends BaseModal { private _titleInfo: PassModalTitleInfo = { titleText: '冷场小白1级', nextTitleProgress: 0, - progressText: '还差3题获得冷场小白2级' + progressText: '还差3题,解锁新成就等级' }; /** 动画起点。为 null 表示不做进度动画,直接展示终态 */ @@ -105,7 +105,7 @@ export class PassModal extends BaseModal { /** 下一步按钮文案,为 null 时保留 prefab 默认值 */ private _nextButtonText: string | null = null; - /** 进度游标 0% 时的本地 X 坐标,使用 prefab 当前摆放位置作为起点 */ + /** 进度游标 0% 时的本地 X 坐标,根据 ProgressBar Bar 子节点的左端推导出来 */ private _progressAnchorStartX: number | null = null; setParams(params: PassModalParams): void { @@ -400,11 +400,24 @@ export class PassModal extends BaseModal { } private _cacheProgressAnchorStartX(): void { - if (this._progressAnchorStartX !== null || !this.progressAnchor) { + if (this._progressAnchorStartX !== null || !this.titleProgressBar) { return; } - this._progressAnchorStartX = this.progressAnchor.position.x; + const barSprite = this.titleProgressBar.barSprite; + if (!barSprite) { + return; + } + + // Bar 节点 anchor 为 (0, 0.5),其本地 position.x 即为进度条可视左端。 + // ProgressBar 与 ProgressAnchor 共享同一父节点(TitleLevel), + // 因此把 Bar 的本地 X 按 ProgressBar 自身的位移与缩放映射到父节点空间, + // 才是真正的「0% 起点」。直接拿 progressAnchor.position.x 当起点会导致 + // 气泡始终被 prefab 摆放偏移量带跑(实测偏右 ~24px)。 + // -40 为视觉微调,与 PageHome 保持一致。 + const progressBarNode = this.titleProgressBar.node; + const barLocalX = barSprite.node.position.x; + this._progressAnchorStartX = progressBarNode.position.x + barLocalX * progressBarNode.scale.x - 30; } private _resolveProgressAnchor(): void { diff --git a/assets/scripts/utils/AchievementTitleManager.ts b/assets/scripts/utils/AchievementTitleManager.ts index dafbc04..12c12b7 100644 --- a/assets/scripts/utils/AchievementTitleManager.ts +++ b/assets/scripts/utils/AchievementTitleManager.ts @@ -101,7 +101,7 @@ export class AchievementTitleManager { titleText: currentStage.titleText, nextTitleText: nextStage.titleText, nextTitleProgress, - progressText: `还差${remainingToNextTitle}题获得${nextStage.titleText}`, + progressText: `还差${remainingToNextTitle}题,解锁新成就等级`, completedLevelCount, currentTitleStartCount: currentStage.startCount, nextTitleRequiredCount: nextStage.startCount, diff --git a/assets/scripts/utils/ShareLaunchHandler.ts b/assets/scripts/utils/ShareLaunchHandler.ts new file mode 100644 index 0000000..4e2bfc2 --- /dev/null +++ b/assets/scripts/utils/ShareLaunchHandler.ts @@ -0,0 +1,147 @@ +import { WxSDK } from './WxSDK'; +import { ShareManager } from './ShareManager'; +import { AuthManager } from './AuthManager'; +import { ViewManager } from '../core/ViewManager'; + +/** + * 分享启动监听器 + * + * 微信小游戏未被杀掉、只是退到后台时,再次通过好友分享卡片打开小游戏, + * 不会重新走启动链路(PageLoading 不会再跑),因此 `wx.getLaunchOptionsSync()` + * 取到的 query 可能是上一次启动的旧值。这个 handler 通过 `wx.onShow` + * 拿到最新的 query,检测 shareCode 变化后: + * 1. 清掉旧的分享态 + * 2. 调用 `ShareManager.joinShare(code)` 拉取新的题单 + * 3. 直接打开 `PageLevel` 进入分享挑战 + * + * 对应在 PageLevel 那侧通过 `onViewShow` 检测到 ShareManager.shareCode + * 变化,重新走 `_reinitLevelSession`。 + */ +export class ShareLaunchHandler { + private static _instance: ShareLaunchHandler | null = null; + + static get instance(): ShareLaunchHandler { + if (!this._instance) { + this._instance = new ShareLaunchHandler(); + } + return this._instance; + } + + /** 已经处理过的 shareCode,相同则不再重复 join */ + private _activeShareCode: string | null = null; + + /** 是否正在处理一次 onShow 触发的 join 流程,避免并发 */ + private _isHandlingShow: boolean = false; + + /** 是否已经初始化 */ + private _initialized: boolean = false; + + private _showHandler = (res: { query?: Record } | undefined) => { + const code = WxSDK.extractShareCodeFromQuery(res?.query); + if (!code) { + return; + } + // 同一个 shareCode 且当前已经处于该分享态,无需重复处理 + if (code === this._activeShareCode && ShareManager.instance.isShareMode) { + return; + } + // 即使不是新的 code,但如果 ShareManager 已经丢失了分享态(例如挑战完成被 clear), + // 用户重新点同一个分享卡片仍然应当重新加入。 + void this._handleShareCode(code); + }; + + private _hideHandler = () => { + // 目前不在 onHide 时做任何破坏性操作;保留监听只为方便后续扩展 + // (比如:暂停倒计时、上报埋点)。 + console.log('[ShareLaunchHandler] 小游戏切到后台'); + }; + + /** + * 在 main.onLoad 中调用,注册 wx.onShow / wx.onHide。 + * 同时把当前启动参数中的 shareCode 作为种子,避免初次冷启动时 + * 因 wx.onShow 也会被调用一次而重复触发分享流程。 + */ + init(): void { + if (this._initialized) { + return; + } + this._initialized = true; + + if (!WxSDK.isWechat()) { + return; + } + + // 冷启动时先把当前 launch 中的 shareCode 标记成已处理, + // 避免 wx.onShow 在初始展示时拿到同一个 code 又走一遍 join。 + this._activeShareCode = WxSDK.getShareCodeFromLaunch(); + + WxSDK.onAppShow(this._showHandler); + WxSDK.onAppHide(this._hideHandler); + + console.log('[ShareLaunchHandler] 已注册 onShow/onHide 监听'); + } + + /** + * 由 PageLoading 在初始 join 之后调用,把已处理的 shareCode 显式同步过来, + * 让 onShow 收到相同 code 时不会重复 join。 + */ + markActiveShareCode(code: string | null): void { + this._activeShareCode = code; + } + + /** + * 主动取消监听(一般无需调用,留作扩展) + */ + dispose(): void { + if (!this._initialized) return; + this._initialized = false; + WxSDK.offAppShow(this._showHandler); + WxSDK.offAppHide(this._hideHandler); + } + + private async _handleShareCode(code: string): Promise { + if (this._isHandlingShow) { + console.log('[ShareLaunchHandler] 已有 onShow 分享流程在执行,跳过', code); + return; + } + this._isHandlingShow = true; + + try { + console.log('[ShareLaunchHandler] 检测到新的 shareCode,准备切换:', code); + + // 确保已登录(initialize 内部对已有 token 做了校验,幂等可重复调用) + const loginOk = await AuthManager.instance.initialize(); + if (!loginOk) { + console.warn('[ShareLaunchHandler] 登录失败,放弃 onShow 分享切换'); + return; + } + + // 切到新的分享前清掉旧分享态,避免 ShareManager 内残留旧题单导致 PageLevel 错位 + if (ShareManager.instance.isShareMode) { + ShareManager.instance.clearShareMode(); + } + + const joinOk = await ShareManager.instance.joinShare(code); + if (!joinOk) { + console.warn('[ShareLaunchHandler] 加入分享失败:', code); + return; + } + + // 标记当前激活的分享码 + this._activeShareCode = code; + + // 跳过中间页,直接打开 PageLevel 进入分享挑战。 + // PageLevel 会在 onViewShow 中根据 ShareManager.shareCode 与本地缓存比对, + // 决定是否需要 `_reinitLevelSession`。 + ViewManager.instance.open('PageLevel', { + params: { shareMode: true }, + }); + + console.log('[ShareLaunchHandler] 已切换到分享挑战:', code); + } catch (err) { + console.error('[ShareLaunchHandler] 处理 shareCode 异常:', err); + } finally { + this._isHandlingShow = false; + } + } +} diff --git a/assets/scripts/utils/ShareLaunchHandler.ts.meta b/assets/scripts/utils/ShareLaunchHandler.ts.meta new file mode 100644 index 0000000..f222b3d --- /dev/null +++ b/assets/scripts/utils/ShareLaunchHandler.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "4.0.24", + "importer": "typescript", + "imported": true, + "uuid": "0709f849-66c8-4e4c-aec5-044c3c414c3e", + "files": [], + "subMetas": {}, + "userData": {} +} diff --git a/assets/scripts/utils/WxSDK.ts b/assets/scripts/utils/WxSDK.ts index 5f1eb52..46a18ff 100644 --- a/assets/scripts/utils/WxSDK.ts +++ b/assets/scripts/utils/WxSDK.ts @@ -398,15 +398,82 @@ export class WxSDK { try { const options = wxApi.getLaunchOptionsSync(); - if (options?.query?.shareCode) { - console.log('[WxSDK] 检测到分享码:', options.query.shareCode); - return options.query.shareCode; + const code = WxSDK.extractShareCodeFromQuery(options?.query); + if (code) { + console.log('[WxSDK] 检测到分享码:', code); + return code; } } catch (err) { console.warn('[WxSDK] 获取启动参数失败:', err); } return null; } + + /** + * 从查询对象(来自 launch options 或 onShow 回调)中提取 shareCode + */ + static extractShareCodeFromQuery(query: Record | null | undefined): string | null { + const code = query?.shareCode; + return typeof code === 'string' && code.length > 0 ? code : null; + } + + // ==================== 前后台生命周期 ==================== + + /** + * 监听小游戏切到前台事件。 + * 同一个回调可重复注册多次:内部用 wx.onShow,请确保业务层做幂等处理或在卸载时调用 offAppShow。 + * @param callback 切前台时触发,包含本次显示对应的 query / scene 等参数 + */ + static onAppShow(callback: (res: { query?: Record; scene?: number; path?: string } | undefined) => void): void { + const wxApi = WxSDK.getWx(); + if (!wxApi) return; + + if (typeof wxApi.onShow !== 'function') { + console.warn('[WxSDK] 当前微信版本不支持 onShow'); + return; + } + + wxApi.onShow(callback); + } + + /** + * 取消监听小游戏切到前台事件 + */ + static offAppShow(callback: (res: any) => void): void { + const wxApi = WxSDK.getWx(); + if (!wxApi) return; + + if (typeof wxApi.offShow === 'function') { + wxApi.offShow(callback); + } + } + + /** + * 监听小游戏切到后台事件 + */ + static onAppHide(callback: () => void): void { + const wxApi = WxSDK.getWx(); + if (!wxApi) return; + + if (typeof wxApi.onHide !== 'function') { + console.warn('[WxSDK] 当前微信版本不支持 onHide'); + return; + } + + wxApi.onHide(callback); + } + + /** + * 取消监听小游戏切到后台事件 + */ + static offAppHide(callback: () => void): void { + const wxApi = WxSDK.getWx(); + if (!wxApi) return; + + if (typeof wxApi.offHide === 'function') { + wxApi.offHide(callback); + } + } } // ==================== 隐私授权相关 ==================== diff --git a/settings/mcp-server.json b/settings/mcp-server.json new file mode 100644 index 0000000..3fd74e1 --- /dev/null +++ b/settings/mcp-server.json @@ -0,0 +1,6 @@ +{ + "port": 3008, + "autoStart": false, + "enableDebugLog": false, + "maxConnections": 10 +} \ No newline at end of file diff --git a/settings/tool-manager.json b/settings/tool-manager.json new file mode 100644 index 0000000..38e2266 --- /dev/null +++ b/settings/tool-manager.json @@ -0,0 +1,957 @@ +{ + "configurations": [ + { + "id": "4e401ac4-fa11-4f3b-9531-bb09687de4df", + "name": "默认配置", + "description": "自动创建的默认工具配置", + "tools": [ + { + "category": "scene", + "name": "get_current_scene", + "enabled": true, + "description": "Get current scene information" + }, + { + "category": "scene", + "name": "get_scene_list", + "enabled": true, + "description": "Get all scenes in the project" + }, + { + "category": "scene", + "name": "open_scene", + "enabled": true, + "description": "Open a scene by path" + }, + { + "category": "scene", + "name": "save_scene", + "enabled": true, + "description": "Save current scene" + }, + { + "category": "scene", + "name": "create_scene", + "enabled": true, + "description": "Create a new scene asset" + }, + { + "category": "scene", + "name": "save_scene_as", + "enabled": true, + "description": "Save scene as new file" + }, + { + "category": "scene", + "name": "close_scene", + "enabled": true, + "description": "Close current scene" + }, + { + "category": "scene", + "name": "get_scene_hierarchy", + "enabled": true, + "description": "Get the complete hierarchy of current scene" + }, + { + "category": "node", + "name": "create_node", + "enabled": true, + "description": "Create a new node in the scene. Supports creating empty nodes, nodes with components, or instantiating from assets (prefabs, etc.). IMPORTANT: You should always provide parentUuid to specify where to create the node." + }, + { + "category": "node", + "name": "get_node_info", + "enabled": true, + "description": "Get node information by UUID" + }, + { + "category": "node", + "name": "find_nodes", + "enabled": true, + "description": "Find nodes by name pattern" + }, + { + "category": "node", + "name": "find_node_by_name", + "enabled": true, + "description": "Find first node by exact name" + }, + { + "category": "node", + "name": "get_all_nodes", + "enabled": true, + "description": "Get all nodes in the scene with their UUIDs" + }, + { + "category": "node", + "name": "set_node_property", + "enabled": true, + "description": "Set node property value (prefer using set_node_transform for active/layer/mobility/position/rotation/scale)" + }, + { + "category": "node", + "name": "set_node_transform", + "enabled": true, + "description": "Set node transform properties (position, rotation, scale) with unified interface. Automatically handles 2D/3D node differences." + }, + { + "category": "node", + "name": "delete_node", + "enabled": true, + "description": "Delete a node from scene" + }, + { + "category": "node", + "name": "move_node", + "enabled": true, + "description": "Move node to new parent" + }, + { + "category": "node", + "name": "duplicate_node", + "enabled": true, + "description": "Duplicate a node" + }, + { + "category": "node", + "name": "detect_node_type", + "enabled": true, + "description": "Detect if a node is 2D or 3D based on its components and properties" + }, + { + "category": "component", + "name": "add_component", + "enabled": true, + "description": "Add a component to a specific node. IMPORTANT: You must provide the nodeUuid parameter to specify which node to add the component to." + }, + { + "category": "component", + "name": "remove_component", + "enabled": true, + "description": "Remove a component from a node. componentType must be the component's classId (cid, i.e. the type field from getComponents), not the script name or class name. Use getComponents to get the correct cid." + }, + { + "category": "component", + "name": "get_components", + "enabled": true, + "description": "Get all components of a node" + }, + { + "category": "component", + "name": "get_component_info", + "enabled": true, + "description": "Get specific component information" + }, + { + "category": "component", + "name": "set_component_property", + "enabled": true, + "description": "Set component property values for UI components or custom script components. Supports setting properties of built-in UI components (e.g., cc.Label, cc.Sprite) and custom script components. Note: For node basic properties (name, active, layer, etc.), use set_node_property. For node transform properties (position, rotation, scale, etc.), use set_node_transform." + }, + { + "category": "component", + "name": "attach_script", + "enabled": true, + "description": "Attach a script component to a node" + }, + { + "category": "component", + "name": "get_available_components", + "enabled": true, + "description": "Get list of available component types" + }, + { + "category": "prefab", + "name": "get_prefab_list", + "enabled": true, + "description": "Get all prefabs in the project" + }, + { + "category": "prefab", + "name": "load_prefab", + "enabled": true, + "description": "Load a prefab by path" + }, + { + "category": "prefab", + "name": "instantiate_prefab", + "enabled": true, + "description": "Instantiate a prefab in the scene" + }, + { + "category": "prefab", + "name": "create_prefab", + "enabled": true, + "description": "Create a prefab from a node with all children and components" + }, + { + "category": "prefab", + "name": "update_prefab", + "enabled": true, + "description": "Update an existing prefab" + }, + { + "category": "prefab", + "name": "revert_prefab", + "enabled": true, + "description": "Revert prefab instance to original" + }, + { + "category": "prefab", + "name": "get_prefab_info", + "enabled": true, + "description": "Get detailed prefab information" + }, + { + "category": "prefab", + "name": "validate_prefab", + "enabled": true, + "description": "Validate a prefab file format" + }, + { + "category": "prefab", + "name": "duplicate_prefab", + "enabled": true, + "description": "Duplicate an existing prefab" + }, + { + "category": "prefab", + "name": "restore_prefab_node", + "enabled": true, + "description": "Restore prefab node using prefab asset (built-in undo record)" + }, + { + "category": "project", + "name": "run_project", + "enabled": true, + "description": "Run the project in preview mode" + }, + { + "category": "project", + "name": "build_project", + "enabled": true, + "description": "Build the project" + }, + { + "category": "project", + "name": "get_project_info", + "enabled": true, + "description": "Get project information" + }, + { + "category": "project", + "name": "get_project_settings", + "enabled": true, + "description": "Get project settings" + }, + { + "category": "project", + "name": "refresh_assets", + "enabled": true, + "description": "Refresh asset database" + }, + { + "category": "project", + "name": "import_asset", + "enabled": true, + "description": "Import an asset file" + }, + { + "category": "project", + "name": "get_asset_info", + "enabled": true, + "description": "Get asset information" + }, + { + "category": "project", + "name": "get_assets", + "enabled": true, + "description": "Get assets by type" + }, + { + "category": "project", + "name": "get_build_settings", + "enabled": true, + "description": "Get build settings - shows current limitations" + }, + { + "category": "project", + "name": "open_build_panel", + "enabled": true, + "description": "Open the build panel in the editor" + }, + { + "category": "project", + "name": "check_builder_status", + "enabled": true, + "description": "Check if builder worker is ready" + }, + { + "category": "project", + "name": "start_preview_server", + "enabled": true, + "description": "Start preview server" + }, + { + "category": "project", + "name": "stop_preview_server", + "enabled": true, + "description": "Stop preview server" + }, + { + "category": "project", + "name": "create_asset", + "enabled": true, + "description": "Create a new asset file or folder" + }, + { + "category": "project", + "name": "copy_asset", + "enabled": true, + "description": "Copy an asset to another location" + }, + { + "category": "project", + "name": "move_asset", + "enabled": true, + "description": "Move an asset to another location" + }, + { + "category": "project", + "name": "delete_asset", + "enabled": true, + "description": "Delete an asset" + }, + { + "category": "project", + "name": "save_asset", + "enabled": true, + "description": "Save asset content" + }, + { + "category": "project", + "name": "reimport_asset", + "enabled": true, + "description": "Reimport an asset" + }, + { + "category": "project", + "name": "query_asset_path", + "enabled": true, + "description": "Get asset disk path" + }, + { + "category": "project", + "name": "query_asset_uuid", + "enabled": true, + "description": "Get asset UUID from URL" + }, + { + "category": "project", + "name": "query_asset_url", + "enabled": true, + "description": "Get asset URL from UUID" + }, + { + "category": "project", + "name": "find_asset_by_name", + "enabled": true, + "description": "Find assets by name (supports partial matching and multiple results)" + }, + { + "category": "project", + "name": "get_asset_details", + "enabled": true, + "description": "Get detailed asset information including spriteFrame sub-assets" + }, + { + "category": "debug", + "name": "get_console_logs", + "enabled": true, + "description": "Get editor console logs" + }, + { + "category": "debug", + "name": "clear_console", + "enabled": true, + "description": "Clear editor console" + }, + { + "category": "debug", + "name": "execute_script", + "enabled": true, + "description": "Execute JavaScript in scene context" + }, + { + "category": "debug", + "name": "get_node_tree", + "enabled": true, + "description": "Get detailed node tree for debugging" + }, + { + "category": "debug", + "name": "get_performance_stats", + "enabled": true, + "description": "Get performance statistics" + }, + { + "category": "debug", + "name": "validate_scene", + "enabled": true, + "description": "Validate current scene for issues" + }, + { + "category": "debug", + "name": "get_editor_info", + "enabled": true, + "description": "Get editor and environment information" + }, + { + "category": "debug", + "name": "get_project_logs", + "enabled": true, + "description": "Get project logs from temp/logs/project.log file" + }, + { + "category": "debug", + "name": "get_log_file_info", + "enabled": true, + "description": "Get information about the project log file" + }, + { + "category": "debug", + "name": "search_project_logs", + "enabled": true, + "description": "Search for specific patterns or errors in project logs" + }, + { + "category": "preferences", + "name": "open_preferences_settings", + "enabled": true, + "description": "Open preferences settings panel" + }, + { + "category": "preferences", + "name": "query_preferences_config", + "enabled": true, + "description": "Query preferences configuration" + }, + { + "category": "preferences", + "name": "set_preferences_config", + "enabled": true, + "description": "Set preferences configuration" + }, + { + "category": "preferences", + "name": "get_all_preferences", + "enabled": true, + "description": "Get all available preferences categories" + }, + { + "category": "preferences", + "name": "reset_preferences", + "enabled": true, + "description": "Reset preferences to default values" + }, + { + "category": "preferences", + "name": "export_preferences", + "enabled": true, + "description": "Export current preferences configuration" + }, + { + "category": "preferences", + "name": "import_preferences", + "enabled": true, + "description": "Import preferences configuration from file" + }, + { + "category": "server", + "name": "query_server_ip_list", + "enabled": true, + "description": "Query server IP list" + }, + { + "category": "server", + "name": "query_sorted_server_ip_list", + "enabled": true, + "description": "Get sorted server IP list" + }, + { + "category": "server", + "name": "query_server_port", + "enabled": true, + "description": "Query editor server current port" + }, + { + "category": "server", + "name": "get_server_status", + "enabled": true, + "description": "Get comprehensive server status information" + }, + { + "category": "server", + "name": "check_server_connectivity", + "enabled": true, + "description": "Check server connectivity and network status" + }, + { + "category": "server", + "name": "get_network_interfaces", + "enabled": true, + "description": "Get available network interfaces" + }, + { + "category": "broadcast", + "name": "get_broadcast_log", + "enabled": true, + "description": "Get recent broadcast messages log" + }, + { + "category": "broadcast", + "name": "listen_broadcast", + "enabled": true, + "description": "Start listening for specific broadcast messages" + }, + { + "category": "broadcast", + "name": "stop_listening", + "enabled": true, + "description": "Stop listening for specific broadcast messages" + }, + { + "category": "broadcast", + "name": "clear_broadcast_log", + "enabled": true, + "description": "Clear the broadcast messages log" + }, + { + "category": "broadcast", + "name": "get_active_listeners", + "enabled": true, + "description": "Get list of active broadcast listeners" + }, + { + "category": "sceneAdvanced", + "name": "reset_node_property", + "enabled": true, + "description": "Reset node property to default value" + }, + { + "category": "sceneAdvanced", + "name": "move_array_element", + "enabled": true, + "description": "Move array element position" + }, + { + "category": "sceneAdvanced", + "name": "remove_array_element", + "enabled": true, + "description": "Remove array element at specific index" + }, + { + "category": "sceneAdvanced", + "name": "copy_node", + "enabled": true, + "description": "Copy node for later paste operation" + }, + { + "category": "sceneAdvanced", + "name": "paste_node", + "enabled": true, + "description": "Paste previously copied nodes" + }, + { + "category": "sceneAdvanced", + "name": "cut_node", + "enabled": true, + "description": "Cut node (copy + mark for move)" + }, + { + "category": "sceneAdvanced", + "name": "reset_node_transform", + "enabled": true, + "description": "Reset node position, rotation and scale" + }, + { + "category": "sceneAdvanced", + "name": "reset_component", + "enabled": true, + "description": "Reset component to default values" + }, + { + "category": "sceneAdvanced", + "name": "restore_prefab", + "enabled": true, + "description": "Restore prefab instance from asset" + }, + { + "category": "sceneAdvanced", + "name": "execute_component_method", + "enabled": true, + "description": "Execute method on component" + }, + { + "category": "sceneAdvanced", + "name": "execute_scene_script", + "enabled": true, + "description": "Execute scene script method" + }, + { + "category": "sceneAdvanced", + "name": "scene_snapshot", + "enabled": true, + "description": "Create scene state snapshot" + }, + { + "category": "sceneAdvanced", + "name": "scene_snapshot_abort", + "enabled": true, + "description": "Abort scene snapshot creation" + }, + { + "category": "sceneAdvanced", + "name": "begin_undo_recording", + "enabled": true, + "description": "Begin recording undo data" + }, + { + "category": "sceneAdvanced", + "name": "end_undo_recording", + "enabled": true, + "description": "End recording undo data" + }, + { + "category": "sceneAdvanced", + "name": "cancel_undo_recording", + "enabled": true, + "description": "Cancel undo recording" + }, + { + "category": "sceneAdvanced", + "name": "soft_reload_scene", + "enabled": true, + "description": "Soft reload current scene" + }, + { + "category": "sceneAdvanced", + "name": "query_scene_ready", + "enabled": true, + "description": "Check if scene is ready" + }, + { + "category": "sceneAdvanced", + "name": "query_scene_dirty", + "enabled": true, + "description": "Check if scene has unsaved changes" + }, + { + "category": "sceneAdvanced", + "name": "query_scene_classes", + "enabled": true, + "description": "Query all registered classes" + }, + { + "category": "sceneAdvanced", + "name": "query_scene_components", + "enabled": true, + "description": "Query available scene components" + }, + { + "category": "sceneAdvanced", + "name": "query_component_has_script", + "enabled": true, + "description": "Check if component has script" + }, + { + "category": "sceneAdvanced", + "name": "query_nodes_by_asset_uuid", + "enabled": true, + "description": "Find nodes that use specific asset UUID" + }, + { + "category": "sceneView", + "name": "change_gizmo_tool", + "enabled": true, + "description": "Change Gizmo tool" + }, + { + "category": "sceneView", + "name": "query_gizmo_tool_name", + "enabled": true, + "description": "Get current Gizmo tool name" + }, + { + "category": "sceneView", + "name": "change_gizmo_pivot", + "enabled": true, + "description": "Change transform pivot point" + }, + { + "category": "sceneView", + "name": "query_gizmo_pivot", + "enabled": true, + "description": "Get current Gizmo pivot point" + }, + { + "category": "sceneView", + "name": "query_gizmo_view_mode", + "enabled": true, + "description": "Query view mode (view/select)" + }, + { + "category": "sceneView", + "name": "change_gizmo_coordinate", + "enabled": true, + "description": "Change coordinate system" + }, + { + "category": "sceneView", + "name": "query_gizmo_coordinate", + "enabled": true, + "description": "Get current coordinate system" + }, + { + "category": "sceneView", + "name": "change_view_mode_2d_3d", + "enabled": true, + "description": "Change 2D/3D view mode" + }, + { + "category": "sceneView", + "name": "query_view_mode_2d_3d", + "enabled": true, + "description": "Get current view mode" + }, + { + "category": "sceneView", + "name": "set_grid_visible", + "enabled": true, + "description": "Show/hide grid" + }, + { + "category": "sceneView", + "name": "query_grid_visible", + "enabled": true, + "description": "Query grid visibility status" + }, + { + "category": "sceneView", + "name": "set_icon_gizmo_3d", + "enabled": true, + "description": "Set IconGizmo to 3D or 2D mode" + }, + { + "category": "sceneView", + "name": "query_icon_gizmo_3d", + "enabled": true, + "description": "Query IconGizmo mode" + }, + { + "category": "sceneView", + "name": "set_icon_gizmo_size", + "enabled": true, + "description": "Set IconGizmo size" + }, + { + "category": "sceneView", + "name": "query_icon_gizmo_size", + "enabled": true, + "description": "Query IconGizmo size" + }, + { + "category": "sceneView", + "name": "focus_camera_on_nodes", + "enabled": true, + "description": "Focus scene camera on nodes" + }, + { + "category": "sceneView", + "name": "align_camera_with_view", + "enabled": true, + "description": "Apply scene camera position and angle to selected node" + }, + { + "category": "sceneView", + "name": "align_view_with_node", + "enabled": true, + "description": "Apply selected node position and angle to current view" + }, + { + "category": "sceneView", + "name": "get_scene_view_status", + "enabled": true, + "description": "Get comprehensive scene view status" + }, + { + "category": "sceneView", + "name": "reset_scene_view", + "enabled": true, + "description": "Reset scene view to default settings" + }, + { + "category": "referenceImage", + "name": "add_reference_image", + "enabled": true, + "description": "Add reference image(s) to scene" + }, + { + "category": "referenceImage", + "name": "remove_reference_image", + "enabled": true, + "description": "Remove reference image(s)" + }, + { + "category": "referenceImage", + "name": "switch_reference_image", + "enabled": true, + "description": "Switch to specific reference image" + }, + { + "category": "referenceImage", + "name": "set_reference_image_data", + "enabled": true, + "description": "Set reference image transform and display properties" + }, + { + "category": "referenceImage", + "name": "query_reference_image_config", + "enabled": true, + "description": "Query reference image configuration" + }, + { + "category": "referenceImage", + "name": "query_current_reference_image", + "enabled": true, + "description": "Query current reference image data" + }, + { + "category": "referenceImage", + "name": "refresh_reference_image", + "enabled": true, + "description": "Refresh reference image display" + }, + { + "category": "referenceImage", + "name": "set_reference_image_position", + "enabled": true, + "description": "Set reference image position" + }, + { + "category": "referenceImage", + "name": "set_reference_image_scale", + "enabled": true, + "description": "Set reference image scale" + }, + { + "category": "referenceImage", + "name": "set_reference_image_opacity", + "enabled": true, + "description": "Set reference image opacity" + }, + { + "category": "referenceImage", + "name": "list_reference_images", + "enabled": true, + "description": "List all available reference images" + }, + { + "category": "referenceImage", + "name": "clear_all_reference_images", + "enabled": true, + "description": "Clear all reference images" + }, + { + "category": "assetAdvanced", + "name": "save_asset_meta", + "enabled": true, + "description": "Save asset meta information" + }, + { + "category": "assetAdvanced", + "name": "generate_available_url", + "enabled": true, + "description": "Generate an available URL based on input URL" + }, + { + "category": "assetAdvanced", + "name": "query_asset_db_ready", + "enabled": true, + "description": "Check if asset database is ready" + }, + { + "category": "assetAdvanced", + "name": "open_asset_external", + "enabled": true, + "description": "Open asset with external program" + }, + { + "category": "assetAdvanced", + "name": "batch_import_assets", + "enabled": true, + "description": "Import multiple assets in batch" + }, + { + "category": "assetAdvanced", + "name": "batch_delete_assets", + "enabled": true, + "description": "Delete multiple assets in batch" + }, + { + "category": "assetAdvanced", + "name": "validate_asset_references", + "enabled": true, + "description": "Validate asset references and find broken links" + }, + { + "category": "assetAdvanced", + "name": "get_asset_dependencies", + "enabled": true, + "description": "Get asset dependency tree" + }, + { + "category": "assetAdvanced", + "name": "get_unused_assets", + "enabled": true, + "description": "Find unused assets in project" + }, + { + "category": "assetAdvanced", + "name": "compress_textures", + "enabled": true, + "description": "Batch compress texture assets" + }, + { + "category": "assetAdvanced", + "name": "export_asset_manifest", + "enabled": true, + "description": "Export asset manifest/inventory" + }, + { + "category": "validation", + "name": "validate_json_params", + "enabled": true, + "description": "Validate and fix JSON parameters before sending to other tools" + }, + { + "category": "validation", + "name": "safe_string_value", + "enabled": true, + "description": "Create a safe string value that won't cause JSON parsing issues" + }, + { + "category": "validation", + "name": "format_mcp_request", + "enabled": true, + "description": "Format a complete MCP request with proper JSON escaping" + } + ], + "createdAt": "2026-05-19T14:37:17.528Z", + "updatedAt": "2026-05-19T14:37:17.528Z" + } + ], + "currentConfigId": "4e401ac4-fa11-4f3b-9531-bb09687de4df", + "maxConfigSlots": 5 +} \ No newline at end of file