diff --git a/cli/src/cli/fix-borders-cmd.js b/cli/src/cli/fix-borders-cmd.js new file mode 100644 index 0000000..6b9d87c --- /dev/null +++ b/cli/src/cli/fix-borders-cmd.js @@ -0,0 +1,59 @@ +// ============================================================ +// cli/fix-borders-cmd.js — fix-borders 子命令 +// +// 用法: +// cocos-mcp-cli fix-borders [--project ] [--dry-run] +// +// 修复 Cocos 重启把图片九宫格 border(subMetas.*.userData.border*)重置成 0 的 meta: +// 扫 git 改动的 .meta,把「git 里有值、工作区被清 0」的 border 还原成 git 的值。 +// 只动 border 字段、保留 meta 其它改动。 +// --dry-run 只预览不写;--project 指定项目路径(默认当前目录)。 +// ============================================================ + +'use strict'; + +const { fixResetBorders } = require('../editor/fix-borders.js'); + +function die(msg) { process.stderr.write('Error: ' + msg + '\n'); process.exit(1); } + +function cmdFixBorders(argv) { + let project = process.cwd(); + let dryRun = false; + + for (let i = 0; i < argv.length; i++) { + const a = argv[i]; + if (a === '--dry-run') { + dryRun = true; + } else if (a === '--project' || a === '-p') { + project = argv[++i]; + } else if (a === '--help' || a === '-h') { + process.stdout.write( + 'cocos-mcp-cli fix-borders — 修复 Cocos 重启把九宫格 border 重置成 0 的 meta\n\n' + + ' --project, -p 项目路径(git 仓库或其子目录,默认当前目录)\n' + + ' --dry-run 只预览将还原哪些,不写文件\n\n' + + '原理:扫 git 改动的 .meta,把「git 有值、工作区被清 0」的 border 还原成 git 的值。\n'); + return; + } else { + die('未知参数 "' + a + '"'); + } + } + + let r; + try { + r = fixResetBorders(project, { dryRun: dryRun }); + } catch (e) { + die(e.message); + } + + process.stdout.write( + (dryRun ? '[dry-run] ' : '') + + '扫描 ' + r.scanned + ' 个改动 meta,还原 border:' + + r.fixedFiles + ' 个 meta / ' + r.fixedFrames + ' 个 sprite-frame\n'); + + if (r.details.length) { + process.stdout.write(r.details.slice(0, 50).join('\n') + '\n'); + if (r.details.length > 50) process.stdout.write('...(共 ' + r.details.length + ' 处,此处只列前 50)\n'); + } +} + +module.exports = { cmdFixBorders }; diff --git a/cli/src/cli/main.js b/cli/src/cli/main.js index 8a4b3b8..13c5d99 100644 --- a/cli/src/cli/main.js +++ b/cli/src/cli/main.js @@ -20,6 +20,7 @@ const { cmdExtractPrefab } = require('./extract-cmd.js'); const { cmdCompactPrefab } = require('./compact-cmd.js'); const { cmdEnsureMeta } = require('./ensure-meta-cmd.js'); const { cmdBuild } = require('./build-cmd.js'); +const { cmdFixBorders } = require('./fix-borders-cmd.js'); function die(msg) { process.stderr.write('Error: ' + msg + '\n'); @@ -55,8 +56,10 @@ function main(argv) { cmdEnsureMeta(rest); } else if (cmd === 'build') { cmdBuild(rest); + } else if (cmd === 'fix-borders') { + cmdFixBorders(rest); } else { - die(`未知子命令 "${cmd}",可用: query / set / batch / anim / diff / create-prefab / extract-prefab / compact-prefab / ensure-meta / build`); + die(`未知子命令 "${cmd}",可用: query / set / batch / anim / diff / create-prefab / extract-prefab / compact-prefab / ensure-meta / build / fix-borders`); } } diff --git a/cli/src/editor/fix-borders.js b/cli/src/editor/fix-borders.js new file mode 100644 index 0000000..168b20c --- /dev/null +++ b/cli/src/editor/fix-borders.js @@ -0,0 +1,99 @@ +// ============================================================ +// editor/fix-borders.js — 修复 Cocos 重启把图片九宫格 border 重置成 0 的 meta +// +// 现象:Cocos 重启/重新导入有时把已设九宫格的图 meta 里 +// subMetas..userData.{borderTop,borderBottom,borderLeft,borderRight} +// 重置成 0(九宫格丢失)。 +// +// 修复:扫 git 工作区改动的 .meta,找出「git 版本 border 非 0、工作区被重置成 0」的, +// 把 git 的 border 值还原回去。靠 git 对比拿正确值,只动 border 四个字段、 +// 保留 meta 其它内容;写回用 Cocos 标准格式(2 空格缩进 + 尾随换行)。 +// +// 纯 Node + git,跨平台。供 CLI(fix-borders-cmd)和 MCP tool 共用。 +// ============================================================ + +'use strict'; + +const fs = require('fs'); +const path = require('path'); +const cp = require('child_process'); + +const BORDER_KEYS = ['borderTop', 'borderBottom', 'borderLeft', 'borderRight']; + +function git(root, args) { + return cp.execFileSync('git', ['-C', root].concat(args), { + encoding: 'utf8', + maxBuffer: 64 * 1024 * 1024, + }); +} + +// 四个 border 都为 0 / 缺失(被重置的特征) +function allZero(ud) { return BORDER_KEYS.every(function (b) { return !ud[b]; }); } +// 至少一个 border 非 0(git 里有正确九宫格值的特征) +function anyNonZero(ud) { return BORDER_KEYS.some(function (b) { return !!ud[b]; }); } + +/** + * 扫 git 改动的 .meta,还原被 Cocos 重置成 0 的九宫格 border。 + * @param {string} projectOrRoot 项目路径(git 仓库或其子目录均可) + * @param {{dryRun?:boolean}} [opts] + * @returns {{gitRoot:string, scanned:number, fixedFiles:number, fixedFrames:number, details:string[]}} + */ +function fixResetBorders(projectOrRoot, opts) { + opts = opts || {}; + var dryRun = !!opts.dryRun; + + var gitRoot; + try { + gitRoot = git(projectOrRoot, ['rev-parse', '--show-toplevel']).trim(); + } catch (e) { + throw new Error('不是 git 仓库(或 git 不可用): ' + projectOrRoot); + } + + var changed = git(gitRoot, ['diff', '--name-only', '--', '*.meta']) + .split('\n').map(function (s) { return s.trim(); }).filter(Boolean); + + var fixedFiles = 0, fixedFrames = 0; + var details = []; + + for (var i = 0; i < changed.length; i++) { + var rel = changed[i]; + var abs = path.join(gitRoot, rel); + if (!fs.existsSync(abs)) continue; + + var work, head; + try { work = JSON.parse(fs.readFileSync(abs, 'utf8')); } catch (e) { continue; } + try { head = JSON.parse(git(gitRoot, ['show', 'HEAD:' + rel])); } catch (e) { continue; } // git 里没有(新文件)跳过 + + var wSubs = work && work.subMetas; + var hSubs = head && head.subMetas; + if (!wSubs || !hSubs || typeof wSubs !== 'object' || typeof hSubs !== 'object') continue; + + var metaChanged = false; + var keys = Object.keys(wSubs); + for (var k = 0; k < keys.length; k++) { + var key = keys[k]; + var wud = wSubs[key] && wSubs[key].userData; + var hud = hSubs[key] && hSubs[key].userData; + if (!wud || !hud) continue; + if (!BORDER_KEYS.some(function (b) { return b in wud; })) continue; + + // 工作区 border 全 0、git 版本有非 0 → 被重置,还原 git 的值 + if (allZero(wud) && anyNonZero(hud)) { + for (var b = 0; b < BORDER_KEYS.length; b++) wud[BORDER_KEYS[b]] = hud[BORDER_KEYS[b]]; + metaChanged = true; + fixedFrames++; + details.push(rel + ' [' + key + '] → T' + hud.borderTop + '/B' + hud.borderBottom + + '/L' + hud.borderLeft + '/R' + hud.borderRight); + } + } + + if (metaChanged) { + fixedFiles++; + if (!dryRun) fs.writeFileSync(abs, JSON.stringify(work, null, 2) + '\n', 'utf8'); + } + } + + return { gitRoot: gitRoot, scanned: changed.length, fixedFiles: fixedFiles, fixedFrames: fixedFrames, details: details }; +} + +module.exports = { fixResetBorders: fixResetBorders }; diff --git a/main.js b/main.js index acac90a..4cb636a 100644 --- a/main.js +++ b/main.js @@ -488,6 +488,10 @@ function buildToolCtx() { cleanDevDir: function () { return exports.methods.cleanDevDir(); }, getStatus: function () { return exports.methods.getStatus(); }, reloadPackage: doRestartSelf, + fixResetBorders: function (opts) { + var mod = require('./cli/src/editor/fix-borders.js'); + return mod.fixResetBorders(Editor.Project.path, opts || {}); + }, }, }; } diff --git a/server/tools.js b/server/tools.js index 706d2e4..e1987e5 100644 --- a/server/tools.js +++ b/server/tools.js @@ -308,6 +308,17 @@ function defineTools(ctx) { return local.cleanDevDir(); }, }, + { + name: 'meta_fix_reset_border', + description: '修复 Cocos 重启把图片九宫格 border(subMetas.*.userData.border*)重置成 0 的 meta:扫 git 改动的 .meta,把「git 有值、工作区被清 0」的 border 还原成 git 的正确值,只动 border 字段、保留 meta 其它改动。dryRun=true 只预览不写。', + inputSchema: { + type: 'object', + properties: { dryRun: { type: 'boolean', description: 'true=只预览不写文件,列出将还原的 meta' } }, + }, + handler: async function (args) { + return local.fixResetBorders({ dryRun: !!(args && args.dryRun) }); + }, + }, ]; }