Compare commits

...

4 Commits

8 changed files with 208 additions and 116 deletions

7
.gitattributes vendored Normal file
View File

@@ -0,0 +1,7 @@
* text=auto eol=lf
*.png binary
*.jpg binary
*.jpeg binary
*.gif binary
*.ico binary

32
dist/scene.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -141,6 +141,7 @@
"scene": { "scene": {
"script": "./dist/scene.js", "script": "./dist/scene.js",
"methods": [ "methods": [
"executeScript",
"createNewScene", "createNewScene",
"addComponentToNode", "addComponentToNode",
"removeComponentFromNode", "removeComponentFromNode",

View File

@@ -2,6 +2,30 @@ import { join } from 'path';
module.paths.push(join(Editor.App.path, 'node_modules')); module.paths.push(join(Editor.App.path, 'node_modules'));
export const methods: { [key: string]: (...any: any) => any } = { export const methods: { [key: string]: (...any: any) => any } = {
/**
* Execute arbitrary JavaScript in the scene process.
* `cc` is available in scope; the value of the last expression is returned
* (compatible with `(function(){ ... })();` IIFE scripts).
*/
executeScript(script: string) {
try {
const cc = require('cc');
const body = String(script).trim().replace(/;+\s*$/, '');
let fn: Function;
try {
fn = new Function('cc', 'return (' + body + '\n);');
} catch (_e) {
fn = new Function('cc', script);
}
const result = fn(cc);
let data: any = result;
try { JSON.stringify(result); } catch (_) { data = String(result); }
return { success: true, data: { result: data } };
} catch (error: any) {
return { success: false, error: error.message };
}
},
/** /**
* Create a new scene * Create a new scene
*/ */

View File

@@ -255,17 +255,11 @@ export class DebugTools implements ToolExecutor {
private async executeScript(script: string): Promise<ToolResponse> { private async executeScript(script: string): Promise<ToolResponse> {
return new Promise((resolve) => { return new Promise((resolve) => {
Editor.Message.request('scene', 'execute-scene-script', { Editor.Message.request('scene', 'execute-scene-script', {
name: 'console', name: 'cocos-mcp-server',
method: 'eval', method: 'executeScript',
args: [script] args: [script]
}).then((result: any) => { }).then((result: any) => {
resolve({ resolve(result);
success: true,
data: {
result: result,
message: 'Script executed successfully'
}
});
}).catch((err: Error) => { }).catch((err: Error) => {
resolve({ success: false, error: err.message }); resolve({ success: false, error: err.message });
}); });
@@ -274,52 +268,69 @@ export class DebugTools implements ToolExecutor {
private async getNodeTree(rootUuid?: string, maxDepth: number = 10): Promise<ToolResponse> { private async getNodeTree(rootUuid?: string, maxDepth: number = 10): Promise<ToolResponse> {
return new Promise((resolve) => { return new Promise((resolve) => {
const buildTree = async (nodeUuid: string, depth: number = 0): Promise<any> => { const mapTree = (node: any, depth: number = 0): any => {
if (depth >= maxDepth) { if (depth >= maxDepth) {
return { truncated: true }; return { truncated: true };
} }
if (!node) {
try { return null;
const nodeData = await Editor.Message.request('scene', 'query-node', nodeUuid); }
const children = Array.isArray(node.children) ? node.children : [];
const tree = { return {
uuid: nodeData.uuid, uuid: node.uuid,
name: nodeData.name, name: node.name,
active: nodeData.active, active: node.active,
components: (nodeData as any).components ? (nodeData as any).components.map((c: any) => c.__type__) : [], components: Array.isArray(node.components)
childCount: nodeData.children ? nodeData.children.length : 0, ? node.components.map((c: any) => c.__type__ || c.type || c.cid).filter((x: any) => x)
children: [] as any[] : [],
childCount: children.length,
children: children.map((c: any) => mapTree(c, depth + 1))
};
}; };
if (nodeData.children && nodeData.children.length > 0) { const findInTree = (node: any, uuid: string): any => {
for (const childId of nodeData.children) { if (!node) {
const childTree = await buildTree(childId, depth + 1); return null;
tree.children.push(childTree); }
if (node.uuid === uuid) {
return node;
}
const children = Array.isArray(node.children) ? node.children : [];
for (const child of children) {
const found = findInTree(child, uuid);
if (found) {
return found;
} }
} }
return null;
return tree;
} catch (err: any) {
return { error: err.message };
}
}; };
// 'query-hierarchy' 在 3.8.x 不存在,统一使用已验证可用的 'query-node-tree'
Editor.Message.request('scene', 'query-node-tree').then((tree: any) => {
if (rootUuid) { if (rootUuid) {
buildTree(rootUuid).then(tree => { const roots = Array.isArray(tree) ? tree : [tree];
resolve({ success: true, data: tree }); let target: any = null;
}); for (const root of roots) {
} else { target = findInTree(root, rootUuid);
Editor.Message.request('scene', 'query-hierarchy').then(async (hierarchy: any) => { if (target) {
const trees = []; break;
for (const rootNode of hierarchy.children) {
const tree = await buildTree(rootNode.uuid);
trees.push(tree);
} }
}
if (!target) {
resolve({ success: false, error: `Node not found: ${rootUuid}` });
return;
}
resolve({ success: true, data: mapTree(target, 0) });
} else {
const roots = Array.isArray(tree)
? tree
: (tree && Array.isArray(tree.children) ? tree.children : (tree ? [tree] : []));
const trees = roots.map((root: any) => mapTree(root, 0));
resolve({ success: true, data: trees }); resolve({ success: true, data: trees });
}
}).catch((err: Error) => { }).catch((err: Error) => {
resolve({ success: false, error: err.message }); resolve({ success: false, error: err.message });
}); });
}
}); });
} }
@@ -365,8 +376,11 @@ export class DebugTools implements ToolExecutor {
// Check for performance issues // Check for performance issues
if (options.checkPerformance) { if (options.checkPerformance) {
const hierarchy = await Editor.Message.request('scene', 'query-hierarchy'); const hierarchy: any = await Editor.Message.request('scene', 'query-node-tree');
const nodeCount = this.countNodes(hierarchy.children); const roots = Array.isArray(hierarchy)
? hierarchy
: (hierarchy && Array.isArray(hierarchy.children) ? hierarchy.children : []);
const nodeCount = this.countNodes(roots);
if (nodeCount > 1000) { if (nodeCount > 1000) {
issues.push({ issues.push({

View File

@@ -233,20 +233,20 @@ export class PrefabTools implements ToolExecutor {
private async loadPrefab(prefabPath: string): Promise<ToolResponse> { private async loadPrefab(prefabPath: string): Promise<ToolResponse> {
return new Promise((resolve) => { return new Promise((resolve) => {
Editor.Message.request('asset-db', 'query-asset-info', prefabPath).then((assetInfo: any) => { let assetInfo: any = null;
if (!assetInfo) { Editor.Message.request('asset-db', 'query-asset-info', prefabPath).then((info: any) => {
if (!info) {
throw new Error('Prefab not found'); throw new Error('Prefab not found');
} }
assetInfo = info;
return Editor.Message.request('scene', 'load-asset', { return Editor.Message.request('scene', 'open-scene', info.uuid);
uuid: assetInfo.uuid }).then(() => {
});
}).then((prefabData: any) => {
resolve({ resolve({
success: true, success: true,
data: { data: {
uuid: prefabData.uuid, uuid: assetInfo.uuid,
name: prefabData.name, name: assetInfo.name,
message: 'Prefab loaded successfully' message: 'Prefab loaded successfully'
} }
}); });