359 lines
14 KiB
TypeScript
359 lines
14 KiB
TypeScript
import { ToolDefinition, ToolResponse, ToolExecutor, PrefabInfo } from '../types';
|
||
|
||
export class PrefabTools implements ToolExecutor {
|
||
getTools(): ToolDefinition[] {
|
||
return [
|
||
{
|
||
name: 'get_prefab_list',
|
||
description: 'Get all prefabs in the project',
|
||
inputSchema: {
|
||
type: 'object',
|
||
properties: {
|
||
folder: {
|
||
type: 'string',
|
||
description: 'Folder path to search (optional)',
|
||
default: 'db://assets'
|
||
}
|
||
}
|
||
}
|
||
},
|
||
{
|
||
name: 'load_prefab',
|
||
description: 'Load a prefab by path',
|
||
inputSchema: {
|
||
type: 'object',
|
||
properties: {
|
||
prefabPath: {
|
||
type: 'string',
|
||
description: 'Prefab asset path'
|
||
}
|
||
},
|
||
required: ['prefabPath']
|
||
}
|
||
},
|
||
{
|
||
name: 'instantiate_prefab',
|
||
description: 'Instantiate a prefab in the scene',
|
||
inputSchema: {
|
||
type: 'object',
|
||
properties: {
|
||
prefabPath: {
|
||
type: 'string',
|
||
description: 'Prefab asset path'
|
||
},
|
||
parentUuid: {
|
||
type: 'string',
|
||
description: 'Parent node UUID (optional)'
|
||
},
|
||
position: {
|
||
type: 'object',
|
||
description: 'Initial position',
|
||
properties: {
|
||
x: { type: 'number' },
|
||
y: { type: 'number' },
|
||
z: { type: 'number' }
|
||
}
|
||
}
|
||
},
|
||
required: ['prefabPath']
|
||
}
|
||
},
|
||
{
|
||
name: 'create_prefab',
|
||
description: 'Create a prefab from a node',
|
||
inputSchema: {
|
||
type: 'object',
|
||
properties: {
|
||
nodeUuid: {
|
||
type: 'string',
|
||
description: 'Source node UUID'
|
||
},
|
||
savePath: {
|
||
type: 'string',
|
||
description: 'Path to save the prefab'
|
||
},
|
||
prefabName: {
|
||
type: 'string',
|
||
description: 'Prefab name'
|
||
}
|
||
},
|
||
required: ['nodeUuid', 'savePath', 'prefabName']
|
||
}
|
||
},
|
||
{
|
||
name: 'create_prefab_from_node',
|
||
description: 'Create a prefab from a node (alias for create_prefab)',
|
||
inputSchema: {
|
||
type: 'object',
|
||
properties: {
|
||
nodeUuid: {
|
||
type: 'string',
|
||
description: 'Source node UUID'
|
||
},
|
||
prefabPath: {
|
||
type: 'string',
|
||
description: 'Path to save the prefab'
|
||
}
|
||
},
|
||
required: ['nodeUuid', 'prefabPath']
|
||
}
|
||
},
|
||
{
|
||
name: 'update_prefab',
|
||
description: 'Update an existing prefab',
|
||
inputSchema: {
|
||
type: 'object',
|
||
properties: {
|
||
prefabPath: {
|
||
type: 'string',
|
||
description: 'Prefab asset path'
|
||
},
|
||
nodeUuid: {
|
||
type: 'string',
|
||
description: 'Node UUID with changes'
|
||
}
|
||
},
|
||
required: ['prefabPath', 'nodeUuid']
|
||
}
|
||
},
|
||
{
|
||
name: 'revert_prefab',
|
||
description: 'Revert prefab instance to original',
|
||
inputSchema: {
|
||
type: 'object',
|
||
properties: {
|
||
nodeUuid: {
|
||
type: 'string',
|
||
description: 'Prefab instance node UUID'
|
||
}
|
||
},
|
||
required: ['nodeUuid']
|
||
}
|
||
},
|
||
{
|
||
name: 'get_prefab_info',
|
||
description: 'Get detailed prefab information',
|
||
inputSchema: {
|
||
type: 'object',
|
||
properties: {
|
||
prefabPath: {
|
||
type: 'string',
|
||
description: 'Prefab asset path'
|
||
}
|
||
},
|
||
required: ['prefabPath']
|
||
}
|
||
}
|
||
];
|
||
}
|
||
|
||
async execute(toolName: string, args: any): Promise<ToolResponse> {
|
||
switch (toolName) {
|
||
case 'get_prefab_list':
|
||
return await this.getPrefabList(args.folder);
|
||
case 'load_prefab':
|
||
return await this.loadPrefab(args.prefabPath);
|
||
case 'instantiate_prefab':
|
||
return await this.instantiatePrefab(args);
|
||
case 'create_prefab':
|
||
return await this.createPrefab(args);
|
||
case 'create_prefab_from_node':
|
||
return await this.createPrefabFromNode(args);
|
||
case 'update_prefab':
|
||
return await this.updatePrefab(args.prefabPath, args.nodeUuid);
|
||
case 'revert_prefab':
|
||
return await this.revertPrefab(args.nodeUuid);
|
||
case 'get_prefab_info':
|
||
return await this.getPrefabInfo(args.prefabPath);
|
||
default:
|
||
throw new Error(`Unknown tool: ${toolName}`);
|
||
}
|
||
}
|
||
|
||
private async getPrefabList(folder: string = 'db://assets'): Promise<ToolResponse> {
|
||
return new Promise((resolve) => {
|
||
const pattern = folder.endsWith('/') ?
|
||
`${folder}**/*.prefab` : `${folder}/**/*.prefab`;
|
||
|
||
Editor.Message.request('asset-db', 'query-assets', {
|
||
pattern: pattern
|
||
}).then((results: any[]) => {
|
||
const prefabs: PrefabInfo[] = results.map(asset => ({
|
||
name: asset.name,
|
||
path: asset.url,
|
||
uuid: asset.uuid,
|
||
folder: asset.url.substring(0, asset.url.lastIndexOf('/'))
|
||
}));
|
||
resolve({ success: true, data: prefabs });
|
||
}).catch((err: Error) => {
|
||
resolve({ success: false, error: err.message });
|
||
});
|
||
});
|
||
}
|
||
|
||
private async loadPrefab(prefabPath: string): Promise<ToolResponse> {
|
||
return new Promise((resolve) => {
|
||
Editor.Message.request('asset-db', 'query-asset-info', prefabPath).then((assetInfo: any) => {
|
||
if (!assetInfo) {
|
||
throw new Error('Prefab not found');
|
||
}
|
||
|
||
return Editor.Message.request('scene', 'load-asset', {
|
||
uuid: assetInfo.uuid
|
||
});
|
||
}).then((prefabData: any) => {
|
||
resolve({
|
||
success: true,
|
||
data: {
|
||
uuid: prefabData.uuid,
|
||
name: prefabData.name,
|
||
message: 'Prefab loaded successfully'
|
||
}
|
||
});
|
||
}).catch((err: Error) => {
|
||
resolve({ success: false, error: err.message });
|
||
});
|
||
});
|
||
}
|
||
|
||
private async instantiatePrefab(args: any): Promise<ToolResponse> {
|
||
return new Promise((resolve) => {
|
||
Editor.Message.request('asset-db', 'query-asset-info', args.prefabPath).then((assetInfo: any) => {
|
||
if (!assetInfo) {
|
||
throw new Error('Prefab not found');
|
||
}
|
||
|
||
const instantiateData: any = {
|
||
prefab: assetInfo.uuid
|
||
};
|
||
|
||
if (args.parentUuid) {
|
||
instantiateData.parent = args.parentUuid;
|
||
}
|
||
|
||
if (args.position) {
|
||
instantiateData.position = args.position;
|
||
}
|
||
|
||
return Editor.Message.request('scene', 'instantiate-prefab', instantiateData);
|
||
}).then((result: any) => {
|
||
resolve({
|
||
success: true,
|
||
data: {
|
||
nodeUuid: result.uuid,
|
||
name: result.name,
|
||
message: 'Prefab instantiated successfully'
|
||
}
|
||
});
|
||
}).catch((err: Error) => {
|
||
resolve({ success: false, error: err.message });
|
||
});
|
||
});
|
||
}
|
||
|
||
private async createPrefab(args: any): Promise<ToolResponse> {
|
||
return new Promise((resolve) => {
|
||
// 支持 prefabPath 和 savePath 两种参数名
|
||
const pathParam = args.prefabPath || args.savePath;
|
||
if (!pathParam) {
|
||
resolve({
|
||
success: false,
|
||
error: 'Missing prefab path parameter. Please provide either prefabPath or savePath.'
|
||
});
|
||
return;
|
||
}
|
||
|
||
const fullPath = pathParam.endsWith('.prefab') ?
|
||
pathParam : `${pathParam}/${args.prefabName || 'NewPrefab'}.prefab`;
|
||
|
||
// 预制体创建需要特殊的Editor API支持
|
||
// 目前Cocos Creator 3.8的MCP插件环境下,预制体创建功能受限
|
||
|
||
resolve({
|
||
success: false,
|
||
error: 'Prefab creation is not supported in the current MCP plugin environment',
|
||
instruction: 'Please create prefabs manually by dragging nodes from the scene to the assets folder in the Cocos Creator editor',
|
||
data: {
|
||
nodeUuid: args.nodeUuid,
|
||
requestedPath: fullPath,
|
||
suggestion: 'You can manually drag the node from the scene to the assets folder to create a prefab.'
|
||
}
|
||
});
|
||
});
|
||
}
|
||
|
||
private async updatePrefab(prefabPath: string, nodeUuid: string): Promise<ToolResponse> {
|
||
return new Promise((resolve) => {
|
||
Editor.Message.request('asset-db', 'query-asset-info', prefabPath).then((assetInfo: any) => {
|
||
if (!assetInfo) {
|
||
throw new Error('Prefab not found');
|
||
}
|
||
|
||
return Editor.Message.request('scene', 'apply-prefab', {
|
||
node: nodeUuid,
|
||
prefab: assetInfo.uuid
|
||
});
|
||
}).then(() => {
|
||
resolve({
|
||
success: true,
|
||
message: 'Prefab updated successfully'
|
||
});
|
||
}).catch((err: Error) => {
|
||
resolve({ success: false, error: err.message });
|
||
});
|
||
});
|
||
}
|
||
|
||
private async revertPrefab(nodeUuid: string): Promise<ToolResponse> {
|
||
return new Promise((resolve) => {
|
||
Editor.Message.request('scene', 'revert-prefab', {
|
||
node: nodeUuid
|
||
}).then(() => {
|
||
resolve({
|
||
success: true,
|
||
message: 'Prefab instance reverted successfully'
|
||
});
|
||
}).catch((err: Error) => {
|
||
resolve({ success: false, error: err.message });
|
||
});
|
||
});
|
||
}
|
||
|
||
private async getPrefabInfo(prefabPath: string): Promise<ToolResponse> {
|
||
return new Promise((resolve) => {
|
||
Editor.Message.request('asset-db', 'query-asset-info', prefabPath).then((assetInfo: any) => {
|
||
if (!assetInfo) {
|
||
throw new Error('Prefab not found');
|
||
}
|
||
|
||
return Editor.Message.request('asset-db', 'query-asset-meta', assetInfo.uuid);
|
||
}).then((metaInfo: any) => {
|
||
const info: PrefabInfo = {
|
||
name: metaInfo.name,
|
||
uuid: metaInfo.uuid,
|
||
path: prefabPath,
|
||
folder: prefabPath.substring(0, prefabPath.lastIndexOf('/')),
|
||
createTime: metaInfo.createTime,
|
||
modifyTime: metaInfo.modifyTime,
|
||
dependencies: metaInfo.depends || []
|
||
};
|
||
resolve({ success: true, data: info });
|
||
}).catch((err: Error) => {
|
||
resolve({ success: false, error: err.message });
|
||
});
|
||
});
|
||
}
|
||
|
||
private async createPrefabFromNode(args: any): Promise<ToolResponse> {
|
||
// 从 prefabPath 提取名称
|
||
const prefabPath = args.prefabPath;
|
||
const prefabName = prefabPath.split('/').pop()?.replace('.prefab', '') || 'NewPrefab';
|
||
|
||
// 调用原来的 createPrefab 方法
|
||
return await this.createPrefab({
|
||
nodeUuid: args.nodeUuid,
|
||
savePath: prefabPath,
|
||
prefabName: prefabName
|
||
});
|
||
}
|
||
} |