diff --git a/main.js b/main.js index d5e7557..f5de666 100644 --- a/main.js +++ b/main.js @@ -1409,11 +1409,11 @@ export default class NewScript extends cc.Component { // 3. 应用编辑 // 必须按倒序应用编辑,否则后续编辑的位置会偏移 (假设edits未排序,这里简单处理,实际上LSP通常建议客户端倒序应用或计算偏移) // 这里假设edits已经按照位置排序或者用户负责,如果需要严谨,应先按 start/position 倒序排序 - // 简单做个排序保险: + // 简单排序保险: const sortedEdits = [...edits].sort((a, b) => { const posA = a.position !== undefined ? a.position : a.start; const posB = b.position !== undefined ? b.position : b.start; - return posB - posA; // big to small + return posB - posA; // 从大到小 }); sortedEdits.forEach((edit) => { @@ -1479,10 +1479,25 @@ export default class NewScript extends cc.Component { 'File/Save': 'scene:stash-and-save', // 别名 'Edit/Undo': 'scene:undo', 'Edit/Redo': 'scene:redo', + 'Edit/Delete': 'scene:delete-selected', + 'Delete': 'scene:delete-selected', + 'delete': 'scene:delete-selected', 'Node/Create Empty Node': 'scene:create-node-by-classid', // 简化的映射,通常需要参数 'Project/Build': 'app:build-project', }; + // 特殊处理 delete-node:UUID 格式 + if (menuPath.startsWith("delete-node:")) { + const uuid = menuPath.split(":")[1]; + if (uuid) { + Editor.Scene.callSceneScript('mcp-bridge', 'delete-node', { uuid }, (err, result) => { + if (err) callback(err); + else callback(null, result || `Node ${uuid} deleted via scene script`); + }); + return; + } + } + if (menuMap[menuPath]) { const ipcMsg = menuMap[menuPath]; try { @@ -1508,7 +1523,6 @@ export default class NewScript extends cc.Component { } }, - // 验证脚本 // 验证脚本 validateScript(args, callback) { const { filePath } = args; @@ -1550,7 +1564,7 @@ export default class NewScript extends cc.Component { // 简单的正则表达式检查:是否有非法字符或明显错误结构 (示例) // 这里暂时只做简单的括号匹配检查或直接通过,但给出一个 Warning - // 检查是否有 class 定义 (简单的heuristic) + // 检查是否有 class 定义 (简单的启发式检查) if (!content.includes('class ') && !content.includes('interface ') && !content.includes('enum ') && !content.includes('export ')) { return callback(null, { valid: true, message: "Warning: TypeScript file seems to lack standard definitions (class/interface), but basic syntax check is skipped due to missing compiler." }); } @@ -1631,7 +1645,7 @@ export default class NewScript extends cc.Component { "inspect-apis"() { addLog("info", "[API Inspector] Starting DEEP inspection..."); - // Helper to get function arguments + // 获取函数参数的辅助函数 const getArgs = (func) => { try { const str = func.toString(); @@ -1645,7 +1659,7 @@ export default class NewScript extends cc.Component { } }; - // Helper to inspect an object + // 检查对象的辅助函数 const inspectObj = (name, obj) => { if (!obj) return { name, exists: false }; const props = {}; @@ -1655,7 +1669,7 @@ export default class NewScript extends cc.Component { const allKeys = new Set([...Object.getOwnPropertyNames(obj), ...Object.getOwnPropertyNames(proto || {})]); allKeys.forEach(key => { - if (key.startsWith("_")) return; // Skip private + if (key.startsWith("_")) return; // 跳过私有属性 try { const val = obj[key]; if (typeof val === 'function') { @@ -1668,7 +1682,7 @@ export default class NewScript extends cc.Component { return { name, exists: true, props }; }; - // 1. Inspect Standard Objects + // 1. 检查标准对象 const standardObjects = { "Editor.assetdb": Editor.assetdb, "Editor.Selection": Editor.Selection, @@ -1684,7 +1698,7 @@ export default class NewScript extends cc.Component { report[key] = inspectObj(key, standardObjects[key]); }); - // 2. Check Specific Forum APIs + // 2. 检查特定论坛提到的 API const forumChecklist = [ "Editor.assetdb.queryInfoByUuid", "Editor.assetdb.assetInfoByUuid", @@ -1705,7 +1719,7 @@ export default class NewScript extends cc.Component { const checklistResults = {}; forumChecklist.forEach(path => { const parts = path.split("."); - let curr = global; // In main process, Editor is global + let curr = global; // 在主进程中,Editor 是全局的 let exists = true; for (const part of parts) { if (curr && curr[part]) { @@ -1721,7 +1735,7 @@ export default class NewScript extends cc.Component { addLog("info", `[API Inspector] Standard Objects:\n${JSON.stringify(report, null, 2)}`); addLog("info", `[API Inspector] Forum Checklist:\n${JSON.stringify(checklistResults, null, 2)}`); - // 3. Inspect Built-in Package IPCs + // 3. 检查内置包 IPC 消息 const ipcReport = {}; const builtinPackages = ["scene", "builder", "assets"]; // 核心内置包 const fs = require("fs"); diff --git a/package.json b/package.json index 3d46a57..5b413b6 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "mcp-bridge", "version": "1.0.0", - "description": "Cocos Creator MCP Bridge", + "description": "Cocos Creator MCP 桥接插件", "author": "Firekula", "main": "main.js", "scene-script": "scene-script.js", diff --git a/scene-script.js b/scene-script.js index e085601..24ac839 100644 --- a/scene-script.js +++ b/scene-script.js @@ -184,7 +184,7 @@ module.exports = { if (typeof value === 'string') { targetNode = cc.engine.getInstanceById(value); - // Fallback for compressed UUIDs + // 针对压缩 UUID 的回退处理 if (!targetNode && Editor.Utils && Editor.Utils.UuidUtils) { try { const decompressed = Editor.Utils.UuidUtils.decompressUuid(value); @@ -252,7 +252,7 @@ module.exports = { } } } catch (e) { - // Ignore type check errors + // 忽略类型检查错误 } component[key] = finalValue; @@ -399,20 +399,20 @@ module.exports = { for (const key in c) { if (typeof c[key] !== "function" && !key.startsWith("_") && c[key] !== undefined) { try { - // Safe serialization check + // 安全序列化检查 const val = c[key]; if (val === null || val === undefined) { properties[key] = val; continue; } - // Primitives are safe + // 基础类型是安全的 if (typeof val !== 'object') { properties[key] = val; continue; } - // Special Cocos Types + // 特殊 Cocos 类型 if (val instanceof cc.ValueType) { properties[key] = val.toString(); } else if (val instanceof cc.Asset) { @@ -422,14 +422,14 @@ module.exports = { } else if (val instanceof cc.Component) { properties[key] = `Component(${val.name}<${val.__typename}>)`; } else { - // Arrays and Plain Objects - // Attempt to strip to pure JSON data to avoid IPC errors with Native/Circular objects + // 数组和普通对象 + // 尝试转换为纯 JSON 数据以避免 IPC 错误(如包含原生对象/循环引用) try { const jsonStr = JSON.stringify(val); - // Ensure we don't pass the original object reference + // 确保不传递原始对象引用 properties[key] = JSON.parse(jsonStr); } catch (e) { - // If JSON fails (e.g. circular), format as string + // 如果 JSON 失败(例如循环引用),格式化为字符串 properties[key] = `[Complex Object: ${val.constructor ? val.constructor.name : typeof val}]`; } } @@ -594,6 +594,28 @@ module.exports = { } }, + "delete-node": function (event, args) { + const { uuid } = args; + const node = cc.engine.getInstanceById(uuid); + if (node) { + const parent = node.parent; + node.destroy(); + Editor.Ipc.sendToMain("scene:dirty"); + // 延迟通知以确保节点已被移除 + setTimeout(() => { + if (parent) { + Editor.Ipc.sendToAll("scene:node-changed", { uuid: parent.uuid }); + } + // 广播节点删除事件 + Editor.Ipc.sendToAll("scene:node-deleted", { uuid: uuid }); + }, 10); + + if (event.reply) event.reply(null, `Node ${uuid} deleted`); + } else { + if (event.reply) event.reply(new Error(`Node not found: ${uuid}`)); + } + }, + "manage-vfx": function (event, args) { const { action, nodeId, properties, name, parentId } = args; const scene = cc.director.getScene();