#
tokens: 33885/50000 1/37 files (page 5/5)
lines: on (toggle) GitHub
raw markdown copy reset
This is page 5 of 5. Use http://codebase.md/daxianlee/cocos-mcp-server?lines=true&page={x} to view the full context.

# Directory Structure

```
├── .gitignore
├── @types
│   └── schema
│       └── package
│           ├── base
│           │   └── panels.json
│           ├── contributions
│           │   └── index.json
│           └── index.json
├── base.tsconfig.json
├── dist
│   ├── examples
│   │   └── prefab-instantiation-example.js
│   ├── main.js
│   ├── mcp-server.js
│   ├── panels
│   │   ├── default
│   │   │   └── index.js
│   │   └── tool-manager
│   │       └── index.js
│   ├── scene.js
│   ├── settings.js
│   ├── test
│   │   ├── manual-test.js
│   │   ├── mcp-tool-tester.js
│   │   ├── prefab-tools-test.js
│   │   └── tool-tester.js
│   ├── tools
│   │   ├── asset-advanced-tools.js
│   │   ├── broadcast-tools.js
│   │   ├── component-tools.js
│   │   ├── debug-tools.js
│   │   ├── node-tools.js
│   │   ├── prefab-tools.js
│   │   ├── preferences-tools.js
│   │   ├── project-tools.js
│   │   ├── reference-image-tools.js
│   │   ├── scene-advanced-tools.js
│   │   ├── scene-tools.js
│   │   ├── scene-view-tools.js
│   │   ├── server-tools.js
│   │   ├── tool-manager.js
│   │   └── validation-tools.js
│   └── types
│       └── index.js
├── FEATURE_GUIDE_CN.md
├── FEATURE_GUIDE_EN.md
├── i18n
│   ├── en.js
│   └── zh.js
├── image
│   ├── iamge2.png
│   └── image-20250717174157957.png
├── package-lock.json
├── package.json
├── README.EN.md
├── README.md
├── scripts
│   └── preinstall.js
├── source
│   ├── main.ts
│   ├── mcp-server.ts
│   ├── panels
│   │   ├── default
│   │   │   └── index.ts
│   │   └── tool-manager
│   │       └── index.ts
│   ├── scene.ts
│   ├── settings.ts
│   ├── test
│   │   ├── manual-test.ts
│   │   ├── mcp-tool-tester.ts
│   │   ├── prefab-tools-test.ts
│   │   └── tool-tester.ts
│   ├── tools
│   │   ├── asset-advanced-tools.ts
│   │   ├── broadcast-tools.ts
│   │   ├── component-tools.ts
│   │   ├── debug-tools.ts
│   │   ├── node-tools.ts
│   │   ├── prefab-tools.ts
│   │   ├── preferences-tools.ts
│   │   ├── project-tools.ts
│   │   ├── reference-image-tools.ts
│   │   ├── scene-advanced-tools.ts
│   │   ├── scene-tools.ts
│   │   ├── scene-view-tools.ts
│   │   ├── server-tools.ts
│   │   ├── tool-manager.ts
│   │   └── validation-tools.ts
│   └── types
│       └── index.ts
├── static
│   ├── icon.png
│   ├── style
│   │   └── default
│   │       └── index.css
│   └── template
│       ├── default
│       │   ├── index.html
│       │   └── tool-manager.html
│       └── vue
│           └── mcp-server-app.html
├── TestScript.js
└── tsconfig.json
```

# Files

--------------------------------------------------------------------------------
/source/tools/prefab-tools.ts:
--------------------------------------------------------------------------------

```typescript
   1 | import { ToolDefinition, ToolResponse, ToolExecutor, PrefabInfo } from '../types';
   2 | 
   3 | export class PrefabTools implements ToolExecutor {
   4 |     getTools(): ToolDefinition[] {
   5 |         return [
   6 |             {
   7 |                 name: 'get_prefab_list',
   8 |                 description: 'Get all prefabs in the project',
   9 |                 inputSchema: {
  10 |                     type: 'object',
  11 |                     properties: {
  12 |                         folder: {
  13 |                             type: 'string',
  14 |                             description: 'Folder path to search (optional)',
  15 |                             default: 'db://assets'
  16 |                         }
  17 |                     }
  18 |                 }
  19 |             },
  20 |             {
  21 |                 name: 'load_prefab',
  22 |                 description: 'Load a prefab by path',
  23 |                 inputSchema: {
  24 |                     type: 'object',
  25 |                     properties: {
  26 |                         prefabPath: {
  27 |                             type: 'string',
  28 |                             description: 'Prefab asset path'
  29 |                         }
  30 |                     },
  31 |                     required: ['prefabPath']
  32 |                 }
  33 |             },
  34 |             {
  35 |                 name: 'instantiate_prefab',
  36 |                 description: 'Instantiate a prefab in the scene',
  37 |                 inputSchema: {
  38 |                     type: 'object',
  39 |                     properties: {
  40 |                         prefabPath: {
  41 |                             type: 'string',
  42 |                             description: 'Prefab asset path'
  43 |                         },
  44 |                         parentUuid: {
  45 |                             type: 'string',
  46 |                             description: 'Parent node UUID (optional)'
  47 |                         },
  48 |                         position: {
  49 |                             type: 'object',
  50 |                             description: 'Initial position',
  51 |                             properties: {
  52 |                                 x: { type: 'number' },
  53 |                                 y: { type: 'number' },
  54 |                                 z: { type: 'number' }
  55 |                             }
  56 |                         }
  57 |                     },
  58 |                     required: ['prefabPath']
  59 |                 }
  60 |             },
  61 |             {
  62 |                 name: 'create_prefab',
  63 |                 description: 'Create a prefab from a node with all children and components',
  64 |                 inputSchema: {
  65 |                     type: 'object',
  66 |                     properties: {
  67 |                         nodeUuid: {
  68 |                             type: 'string',
  69 |                             description: 'Source node UUID'
  70 |                         },
  71 |                         savePath: {
  72 |                             type: 'string',
  73 |                             description: 'Path to save the prefab (e.g., db://assets/prefabs/MyPrefab.prefab)'
  74 |                         },
  75 |                         prefabName: {
  76 |                             type: 'string',
  77 |                             description: 'Prefab name'
  78 |                         }
  79 |                     },
  80 |                     required: ['nodeUuid', 'savePath', 'prefabName']
  81 |                 }
  82 |             },
  83 |             {
  84 |                 name: 'update_prefab',
  85 |                 description: 'Update an existing prefab',
  86 |                 inputSchema: {
  87 |                     type: 'object',
  88 |                     properties: {
  89 |                         prefabPath: {
  90 |                             type: 'string',
  91 |                             description: 'Prefab asset path'
  92 |                         },
  93 |                         nodeUuid: {
  94 |                             type: 'string',
  95 |                             description: 'Node UUID with changes'
  96 |                         }
  97 |                     },
  98 |                     required: ['prefabPath', 'nodeUuid']
  99 |                 }
 100 |             },
 101 |             {
 102 |                 name: 'revert_prefab',
 103 |                 description: 'Revert prefab instance to original',
 104 |                 inputSchema: {
 105 |                     type: 'object',
 106 |                     properties: {
 107 |                         nodeUuid: {
 108 |                             type: 'string',
 109 |                             description: 'Prefab instance node UUID'
 110 |                         }
 111 |                     },
 112 |                     required: ['nodeUuid']
 113 |                 }
 114 |             },
 115 |             {
 116 |                 name: 'get_prefab_info',
 117 |                 description: 'Get detailed prefab information',
 118 |                 inputSchema: {
 119 |                     type: 'object',
 120 |                     properties: {
 121 |                         prefabPath: {
 122 |                             type: 'string',
 123 |                             description: 'Prefab asset path'
 124 |                         }
 125 |                     },
 126 |                     required: ['prefabPath']
 127 |                 }
 128 |             },
 129 |             {
 130 |                 name: 'validate_prefab',
 131 |                 description: 'Validate a prefab file format',
 132 |                 inputSchema: {
 133 |                     type: 'object',
 134 |                     properties: {
 135 |                         prefabPath: {
 136 |                             type: 'string',
 137 |                             description: 'Prefab asset path'
 138 |                         }
 139 |                     },
 140 |                     required: ['prefabPath']
 141 |                 }
 142 |             },
 143 |             {
 144 |                 name: 'duplicate_prefab',
 145 |                 description: 'Duplicate an existing prefab',
 146 |                 inputSchema: {
 147 |                     type: 'object',
 148 |                     properties: {
 149 |                         sourcePrefabPath: {
 150 |                             type: 'string',
 151 |                             description: 'Source prefab path'
 152 |                         },
 153 |                         targetPrefabPath: {
 154 |                             type: 'string',
 155 |                             description: 'Target prefab path'
 156 |                         },
 157 |                         newPrefabName: {
 158 |                             type: 'string',
 159 |                             description: 'New prefab name'
 160 |                         }
 161 |                     },
 162 |                     required: ['sourcePrefabPath', 'targetPrefabPath']
 163 |                 }
 164 |             },
 165 |             {
 166 |                 name: 'restore_prefab_node',
 167 |                 description: 'Restore prefab node using prefab asset (built-in undo record)',
 168 |                 inputSchema: {
 169 |                     type: 'object',
 170 |                     properties: {
 171 |                         nodeUuid: {
 172 |                             type: 'string',
 173 |                             description: 'Prefab instance node UUID'
 174 |                         },
 175 |                         assetUuid: {
 176 |                             type: 'string',
 177 |                             description: 'Prefab asset UUID'
 178 |                         }
 179 |                     },
 180 |                     required: ['nodeUuid', 'assetUuid']
 181 |                 }
 182 |             }
 183 |         ];
 184 |     }
 185 | 
 186 |     async execute(toolName: string, args: any): Promise<ToolResponse> {
 187 |         switch (toolName) {
 188 |             case 'get_prefab_list':
 189 |                 return await this.getPrefabList(args.folder);
 190 |             case 'load_prefab':
 191 |                 return await this.loadPrefab(args.prefabPath);
 192 |             case 'instantiate_prefab':
 193 |                 return await this.instantiatePrefab(args);
 194 |             case 'create_prefab':
 195 |                 return await this.createPrefab(args);
 196 |             case 'update_prefab':
 197 |                 return await this.updatePrefab(args.prefabPath, args.nodeUuid);
 198 |             case 'revert_prefab':
 199 |                 return await this.revertPrefab(args.nodeUuid);
 200 |             case 'get_prefab_info':
 201 |                 return await this.getPrefabInfo(args.prefabPath);
 202 |             case 'validate_prefab':
 203 |                 return await this.validatePrefab(args.prefabPath);
 204 |             case 'duplicate_prefab':
 205 |                 return await this.duplicatePrefab(args);
 206 |             case 'restore_prefab_node':
 207 |                 return await this.restorePrefabNode(args.nodeUuid, args.assetUuid);
 208 |             default:
 209 |                 throw new Error(`Unknown tool: ${toolName}`);
 210 |         }
 211 |     }
 212 | 
 213 |     private async getPrefabList(folder: string = 'db://assets'): Promise<ToolResponse> {
 214 |         return new Promise((resolve) => {
 215 |             const pattern = folder.endsWith('/') ? 
 216 |                 `${folder}**/*.prefab` : `${folder}/**/*.prefab`;
 217 |             
 218 |             Editor.Message.request('asset-db', 'query-assets', {
 219 |                 pattern: pattern
 220 |             }).then((results: any[]) => {
 221 |                 const prefabs: PrefabInfo[] = results.map(asset => ({
 222 |                     name: asset.name,
 223 |                     path: asset.url,
 224 |                     uuid: asset.uuid,
 225 |                     folder: asset.url.substring(0, asset.url.lastIndexOf('/'))
 226 |                 }));
 227 |                 resolve({ success: true, data: prefabs });
 228 |             }).catch((err: Error) => {
 229 |                 resolve({ success: false, error: err.message });
 230 |             });
 231 |         });
 232 |     }
 233 | 
 234 |     private async loadPrefab(prefabPath: string): Promise<ToolResponse> {
 235 |         return new Promise((resolve) => {
 236 |             Editor.Message.request('asset-db', 'query-asset-info', prefabPath).then((assetInfo: any) => {
 237 |                 if (!assetInfo) {
 238 |                     throw new Error('Prefab not found');
 239 |                 }
 240 |                 
 241 |                 return Editor.Message.request('scene', 'load-asset', {
 242 |                     uuid: assetInfo.uuid
 243 |                 });
 244 |             }).then((prefabData: any) => {
 245 |                 resolve({
 246 |                     success: true,
 247 |                     data: {
 248 |                         uuid: prefabData.uuid,
 249 |                         name: prefabData.name,
 250 |                         message: 'Prefab loaded successfully'
 251 |                     }
 252 |                 });
 253 |             }).catch((err: Error) => {
 254 |                 resolve({ success: false, error: err.message });
 255 |             });
 256 |         });
 257 |     }
 258 | 
 259 |     private async instantiatePrefab(args: any): Promise<ToolResponse> {
 260 |         return new Promise(async (resolve) => {
 261 |             try {
 262 |                 // 获取预制体资源信息
 263 |                 const assetInfo = await Editor.Message.request('asset-db', 'query-asset-info', args.prefabPath);
 264 |                 if (!assetInfo) {
 265 |                     throw new Error('预制体未找到');
 266 |                 }
 267 | 
 268 |                 // 使用正确的 create-node API 从预制体资源实例化
 269 |                 const createNodeOptions: any = {
 270 |                     assetUuid: assetInfo.uuid
 271 |                 };
 272 | 
 273 |                 // 设置父节点
 274 |                 if (args.parentUuid) {
 275 |                     createNodeOptions.parent = args.parentUuid;
 276 |                 }
 277 | 
 278 |                 // 设置节点名称
 279 |                 if (args.name) {
 280 |                     createNodeOptions.name = args.name;
 281 |                 } else if (assetInfo.name) {
 282 |                     createNodeOptions.name = assetInfo.name;
 283 |                 }
 284 | 
 285 |                 // 设置初始属性(如位置)
 286 |                 if (args.position) {
 287 |                     createNodeOptions.dump = {
 288 |                         position: {
 289 |                             value: args.position
 290 |                         }
 291 |                     };
 292 |                 }
 293 | 
 294 |                 // 创建节点
 295 |                 const nodeUuid = await Editor.Message.request('scene', 'create-node', createNodeOptions);
 296 |                 const uuid = Array.isArray(nodeUuid) ? nodeUuid[0] : nodeUuid;
 297 | 
 298 |                 // 注意:create-node API从预制体资源创建时应该自动建立预制体关联
 299 |                 console.log('预制体节点创建成功:', {
 300 |                     nodeUuid: uuid,
 301 |                     prefabUuid: assetInfo.uuid,
 302 |                     prefabPath: args.prefabPath
 303 |                 });
 304 |                 
 305 |                 resolve({
 306 |                     success: true,
 307 |                     data: {
 308 |                         nodeUuid: uuid,
 309 |                         prefabPath: args.prefabPath,
 310 |                         parentUuid: args.parentUuid,
 311 |                         position: args.position,
 312 |                         message: '预制体实例化成功,已建立预制体关联'
 313 |                     }
 314 |                 });
 315 |             } catch (err: any) {
 316 |                 resolve({ 
 317 |                     success: false, 
 318 |                     error: `预制体实例化失败: ${err.message}`,
 319 |                     instruction: '请检查预制体路径是否正确,确保预制体文件格式正确'
 320 |                 });
 321 |             }
 322 |         });
 323 |     }
 324 | 
 325 |     /**
 326 |      * 建立节点与预制体的关联关系
 327 |      * 这个方法创建必要的PrefabInfo和PrefabInstance结构
 328 |      */
 329 |     private async establishPrefabConnection(nodeUuid: string, prefabUuid: string, prefabPath: string): Promise<void> {
 330 |         try {
 331 |             // 读取预制体文件获取根节点的fileId
 332 |             const prefabContent = await this.readPrefabFile(prefabPath);
 333 |             if (!prefabContent || !prefabContent.data || !prefabContent.data.length) {
 334 |                 throw new Error('无法读取预制体文件内容');
 335 |             }
 336 | 
 337 |             // 找到预制体根节点的fileId (通常是第二个对象,即索引1)
 338 |             const rootNode = prefabContent.data.find((item: any) => item.__type === 'cc.Node' && item._parent === null);
 339 |             if (!rootNode || !rootNode._prefab) {
 340 |                 throw new Error('无法找到预制体根节点或其预制体信息');
 341 |             }
 342 | 
 343 |             // 获取根节点的PrefabInfo
 344 |             const rootPrefabInfo = prefabContent.data[rootNode._prefab.__id__];
 345 |             if (!rootPrefabInfo || rootPrefabInfo.__type !== 'cc.PrefabInfo') {
 346 |                 throw new Error('无法找到预制体根节点的PrefabInfo');
 347 |             }
 348 | 
 349 |             const rootFileId = rootPrefabInfo.fileId;
 350 | 
 351 |             // 使用scene API建立预制体连接
 352 |             const prefabConnectionData = {
 353 |                 node: nodeUuid,
 354 |                 prefab: prefabUuid,
 355 |                 fileId: rootFileId
 356 |             };
 357 | 
 358 |             // 尝试使用多种API方法建立预制体连接
 359 |             const connectionMethods = [
 360 |                 () => Editor.Message.request('scene', 'connect-prefab-instance', prefabConnectionData),
 361 |                 () => Editor.Message.request('scene', 'set-prefab-connection', prefabConnectionData),
 362 |                 () => Editor.Message.request('scene', 'apply-prefab-link', prefabConnectionData)
 363 |             ];
 364 | 
 365 |             let connected = false;
 366 |             for (const method of connectionMethods) {
 367 |                 try {
 368 |                     await method();
 369 |                     connected = true;
 370 |                     break;
 371 |                 } catch (error) {
 372 |                     console.warn('预制体连接方法失败,尝试下一个方法:', error);
 373 |                 }
 374 |             }
 375 | 
 376 |             if (!connected) {
 377 |                 // 如果所有API方法都失败,尝试手动修改场景数据
 378 |                 console.warn('所有预制体连接API都失败,尝试手动建立连接');
 379 |                 await this.manuallyEstablishPrefabConnection(nodeUuid, prefabUuid, rootFileId);
 380 |             }
 381 | 
 382 |         } catch (error) {
 383 |             console.error('建立预制体连接失败:', error);
 384 |             throw error;
 385 |         }
 386 |     }
 387 | 
 388 |     /**
 389 |      * 手动建立预制体连接(当API方法失败时的备用方案)
 390 |      */
 391 |     private async manuallyEstablishPrefabConnection(nodeUuid: string, prefabUuid: string, rootFileId: string): Promise<void> {
 392 |         try {
 393 |             // 尝试使用dump API修改节点的_prefab属性
 394 |             const prefabConnectionData = {
 395 |                 [nodeUuid]: {
 396 |                     '_prefab': {
 397 |                         '__uuid__': prefabUuid,
 398 |                         '__expectedType__': 'cc.Prefab',
 399 |                         'fileId': rootFileId
 400 |                     }
 401 |                 }
 402 |             };
 403 | 
 404 |             await Editor.Message.request('scene', 'set-property', {
 405 |                 uuid: nodeUuid,
 406 |                 path: '_prefab',
 407 |                 dump: {
 408 |                     value: {
 409 |                         '__uuid__': prefabUuid,
 410 |                         '__expectedType__': 'cc.Prefab'
 411 |                     }
 412 |                 }
 413 |             });
 414 | 
 415 |         } catch (error) {
 416 |             console.error('手动建立预制体连接也失败:', error);
 417 |             // 不抛出错误,因为基本的节点创建已经成功
 418 |         }
 419 |     }
 420 | 
 421 |     /**
 422 |      * 读取预制体文件内容
 423 |      */
 424 |     private async readPrefabFile(prefabPath: string): Promise<any> {
 425 |         try {
 426 |             // 尝试使用asset-db API读取文件内容
 427 |             let assetContent: any;
 428 |             try {
 429 |                 assetContent = await Editor.Message.request('asset-db', 'query-asset-info', prefabPath);
 430 |                 if (assetContent && assetContent.source) {
 431 |                     // 如果有source路径,直接读取文件
 432 |                     const fs = require('fs');
 433 |                     const path = require('path');
 434 |                     const fullPath = path.resolve(assetContent.source);
 435 |                     const fileContent = fs.readFileSync(fullPath, 'utf8');
 436 |                     return JSON.parse(fileContent);
 437 |                 }
 438 |             } catch (error) {
 439 |                 console.warn('使用asset-db读取失败,尝试其他方法:', error);
 440 |             }
 441 | 
 442 |             // 备用方法:转换db://路径为实际文件路径
 443 |             const fsPath = prefabPath.replace('db://assets/', 'assets/').replace('db://assets', 'assets');
 444 |             const fs = require('fs');
 445 |             const path = require('path');
 446 |             
 447 |             // 尝试多个可能的项目根路径
 448 |             const possiblePaths = [
 449 |                 path.resolve(process.cwd(), '../../NewProject_3', fsPath),
 450 |                 path.resolve('/Users/lizhiyong/NewProject_3', fsPath),
 451 |                 path.resolve(fsPath),
 452 |                 // 如果是根目录下的文件,也尝试直接路径
 453 |                 path.resolve('/Users/lizhiyong/NewProject_3/assets', path.basename(fsPath))
 454 |             ];
 455 | 
 456 |             console.log('尝试读取预制体文件,路径转换:', {
 457 |                 originalPath: prefabPath,
 458 |                 fsPath: fsPath,
 459 |                 possiblePaths: possiblePaths
 460 |             });
 461 | 
 462 |             for (const fullPath of possiblePaths) {
 463 |                 try {
 464 |                     console.log(`检查路径: ${fullPath}`);
 465 |                     if (fs.existsSync(fullPath)) {
 466 |                         console.log(`找到文件: ${fullPath}`);
 467 |                         const fileContent = fs.readFileSync(fullPath, 'utf8');
 468 |                         const parsed = JSON.parse(fileContent);
 469 |                         console.log('文件解析成功,数据结构:', {
 470 |                             hasData: !!parsed.data,
 471 |                             dataLength: parsed.data ? parsed.data.length : 0
 472 |                         });
 473 |                         return parsed;
 474 |                     } else {
 475 |                         console.log(`文件不存在: ${fullPath}`);
 476 |                     }
 477 |                 } catch (readError) {
 478 |                     console.warn(`读取文件失败 ${fullPath}:`, readError);
 479 |                 }
 480 |             }
 481 | 
 482 |             throw new Error('无法找到或读取预制体文件');
 483 |         } catch (error) {
 484 |             console.error('读取预制体文件失败:', error);
 485 |             throw error;
 486 |         }
 487 |     }
 488 | 
 489 |     private async tryCreateNodeWithPrefab(args: any): Promise<ToolResponse> {
 490 |         return new Promise((resolve) => {
 491 |             Editor.Message.request('asset-db', 'query-asset-info', args.prefabPath).then((assetInfo: any) => {
 492 |                 if (!assetInfo) {
 493 |                     throw new Error('预制体未找到');
 494 |                 }
 495 | 
 496 |                 // 方法2: 使用 create-node 指定预制体资源
 497 |                 const createNodeOptions: any = {
 498 |                     assetUuid: assetInfo.uuid
 499 |                 };
 500 | 
 501 |                 // 设置父节点
 502 |                 if (args.parentUuid) {
 503 |                     createNodeOptions.parent = args.parentUuid;
 504 |                 }
 505 | 
 506 |                 return Editor.Message.request('scene', 'create-node', createNodeOptions);
 507 |             }).then((nodeUuid: string | string[]) => {
 508 |                 const uuid = Array.isArray(nodeUuid) ? nodeUuid[0] : nodeUuid;
 509 |                 
 510 |                 // 如果指定了位置,设置节点位置
 511 |                 if (args.position && uuid) {
 512 |                     Editor.Message.request('scene', 'set-property', {
 513 |                         uuid: uuid,
 514 |                         path: 'position',
 515 |                         dump: { value: args.position }
 516 |                     }).then(() => {
 517 |                         resolve({
 518 |                             success: true,
 519 |                             data: {
 520 |                                 nodeUuid: uuid,
 521 |                                 prefabPath: args.prefabPath,
 522 |                                 position: args.position,
 523 |                                 message: '预制体实例化成功(备用方法)并设置了位置'
 524 |                             }
 525 |                         });
 526 |                     }).catch(() => {
 527 |                         resolve({
 528 |                             success: true,
 529 |                             data: {
 530 |                                 nodeUuid: uuid,
 531 |                                 prefabPath: args.prefabPath,
 532 |                                 message: '预制体实例化成功(备用方法)但位置设置失败'
 533 |                             }
 534 |                         });
 535 |                     });
 536 |                 } else {
 537 |                     resolve({
 538 |                         success: true,
 539 |                         data: {
 540 |                             nodeUuid: uuid,
 541 |                             prefabPath: args.prefabPath,
 542 |                             message: '预制体实例化成功(备用方法)'
 543 |                         }
 544 |                     });
 545 |                 }
 546 |             }).catch((err: Error) => {
 547 |                 resolve({
 548 |                     success: false,
 549 |                     error: `备用预制体实例化方法也失败: ${err.message}`
 550 |                 });
 551 |             });
 552 |         });
 553 |     }
 554 | 
 555 |     private async tryAlternativeInstantiateMethods(args: any): Promise<ToolResponse> {
 556 |         return new Promise(async (resolve) => {
 557 |             try {
 558 |                 // 方法1: 尝试使用 create-node 然后设置预制体
 559 |                 const assetInfo = await this.getAssetInfo(args.prefabPath);
 560 |                 if (!assetInfo) {
 561 |                     resolve({ success: false, error: '无法获取预制体信息' });
 562 |                     return;
 563 |                 }
 564 | 
 565 |                 // 创建空节点
 566 |                 const createResult = await this.createNode(args.parentUuid, args.position);
 567 |                 if (!createResult.success) {
 568 |                     resolve(createResult);
 569 |                     return;
 570 |                 }
 571 | 
 572 |                 // 尝试将预制体应用到节点
 573 |                 const applyResult = await this.applyPrefabToNode(createResult.data.nodeUuid, assetInfo.uuid);
 574 |                 if (applyResult.success) {
 575 |                     resolve({
 576 |                         success: true,
 577 |                         data: {
 578 |                             nodeUuid: createResult.data.nodeUuid,
 579 |                             name: createResult.data.name,
 580 |                             message: '预制体实例化成功(使用备选方法)'
 581 |                         }
 582 |                     });
 583 |                 } else {
 584 |                     resolve({
 585 |                         success: false,
 586 |                         error: '无法将预制体应用到节点',
 587 |                         data: {
 588 |                             nodeUuid: createResult.data.nodeUuid,
 589 |                             message: '已创建节点,但无法应用预制体数据'
 590 |                         }
 591 |                     });
 592 |                 }
 593 | 
 594 |             } catch (error) {
 595 |                 resolve({ success: false, error: `备选实例化方法失败: ${error}` });
 596 |             }
 597 |         });
 598 |     }
 599 | 
 600 |     private async getAssetInfo(prefabPath: string): Promise<any> {
 601 |         return new Promise((resolve) => {
 602 |             Editor.Message.request('asset-db', 'query-asset-info', prefabPath).then((assetInfo: any) => {
 603 |                 resolve(assetInfo);
 604 |             }).catch(() => {
 605 |                 resolve(null);
 606 |             });
 607 |         });
 608 |     }
 609 | 
 610 |     private async createNode(parentUuid?: string, position?: any): Promise<ToolResponse> {
 611 |         return new Promise((resolve) => {
 612 |             const createNodeOptions: any = {
 613 |                 name: 'PrefabInstance'
 614 |             };
 615 | 
 616 |             // 设置父节点
 617 |             if (parentUuid) {
 618 |                 createNodeOptions.parent = parentUuid;
 619 |             }
 620 | 
 621 |             // 设置位置
 622 |             if (position) {
 623 |                 createNodeOptions.dump = {
 624 |                     position: position
 625 |                 };
 626 |             }
 627 | 
 628 |             Editor.Message.request('scene', 'create-node', createNodeOptions).then((nodeUuid: string | string[]) => {
 629 |                 const uuid = Array.isArray(nodeUuid) ? nodeUuid[0] : nodeUuid;
 630 |                 resolve({
 631 |                     success: true,
 632 |                     data: {
 633 |                         nodeUuid: uuid,
 634 |                         name: 'PrefabInstance'
 635 |                     }
 636 |                 });
 637 |             }).catch((error: any) => {
 638 |                 resolve({ success: false, error: error.message || '创建节点失败' });
 639 |             });
 640 |         });
 641 |     }
 642 | 
 643 |     private async applyPrefabToNode(nodeUuid: string, prefabUuid: string): Promise<ToolResponse> {
 644 |         return new Promise((resolve) => {
 645 |             // 尝试多种方法来应用预制体数据
 646 |             const methods = [
 647 |                 () => Editor.Message.request('scene', 'apply-prefab', { node: nodeUuid, prefab: prefabUuid }),
 648 |                 () => Editor.Message.request('scene', 'set-prefab', { node: nodeUuid, prefab: prefabUuid }),
 649 |                 () => Editor.Message.request('scene', 'load-prefab-to-node', { node: nodeUuid, prefab: prefabUuid })
 650 |             ];
 651 | 
 652 |             const tryMethod = (index: number) => {
 653 |                 if (index >= methods.length) {
 654 |                     resolve({ success: false, error: '无法应用预制体数据' });
 655 |                     return;
 656 |                 }
 657 | 
 658 |                 methods[index]().then(() => {
 659 |                     resolve({ success: true });
 660 |                 }).catch(() => {
 661 |                     tryMethod(index + 1);
 662 |                 });
 663 |             };
 664 | 
 665 |             tryMethod(0);
 666 |         });
 667 |     }
 668 | 
 669 |     /**
 670 |      * 使用 asset-db API 创建预制体的新方法
 671 |      * 深度整合引擎的资源管理系统,实现完整的预制体创建流程
 672 |      */
 673 |     private async createPrefabWithAssetDB(nodeUuid: string, savePath: string, prefabName: string, includeChildren: boolean, includeComponents: boolean): Promise<ToolResponse> {
 674 |         return new Promise(async (resolve) => {
 675 |             try {
 676 |                 console.log('=== 使用 Asset-DB API 创建预制体 ===');
 677 |                 console.log(`节点UUID: ${nodeUuid}`);
 678 |                 console.log(`保存路径: ${savePath}`);
 679 |                 console.log(`预制体名称: ${prefabName}`);
 680 | 
 681 |                 // 第一步:获取节点数据(包括变换属性)
 682 |                 const nodeData = await this.getNodeData(nodeUuid);
 683 |                 if (!nodeData) {
 684 |                     resolve({
 685 |                         success: false,
 686 |                         error: '无法获取节点数据'
 687 |                     });
 688 |                     return;
 689 |                 }
 690 | 
 691 |                 console.log('获取到节点数据,子节点数量:', nodeData.children ? nodeData.children.length : 0);
 692 | 
 693 |                 // 第二步:先创建资源文件以获取引擎分配的UUID
 694 |                 console.log('创建预制体资源文件...');
 695 |                 const tempPrefabContent = JSON.stringify([{"__type__": "cc.Prefab", "_name": prefabName}], null, 2);
 696 |                 const createResult = await this.createAssetWithAssetDB(savePath, tempPrefabContent);
 697 |                 if (!createResult.success) {
 698 |                     resolve(createResult);
 699 |                     return;
 700 |                 }
 701 | 
 702 |                 // 获取引擎分配的实际UUID
 703 |                 const actualPrefabUuid = createResult.data?.uuid;
 704 |                 if (!actualPrefabUuid) {
 705 |                     resolve({
 706 |                         success: false,
 707 |                         error: '无法获取引擎分配的预制体UUID'
 708 |                     });
 709 |                     return;
 710 |                 }
 711 |                 console.log('引擎分配的UUID:', actualPrefabUuid);
 712 | 
 713 |                 // 第三步:使用实际UUID重新生成预制体内容
 714 |                 const prefabContent = await this.createStandardPrefabContent(nodeData, prefabName, actualPrefabUuid, includeChildren, includeComponents);
 715 |                 const prefabContentString = JSON.stringify(prefabContent, null, 2);
 716 | 
 717 |                 // 第四步:更新预制体文件内容
 718 |                 console.log('更新预制体文件内容...');
 719 |                 const updateResult = await this.updateAssetWithAssetDB(savePath, prefabContentString);
 720 |                 
 721 |                 // 第五步:创建对应的meta文件(使用实际UUID)
 722 |                 console.log('创建预制体meta文件...');
 723 |                 const metaContent = this.createStandardMetaContent(prefabName, actualPrefabUuid);
 724 |                 const metaResult = await this.createMetaWithAssetDB(savePath, metaContent);
 725 |                 
 726 |                 // 第六步:重新导入资源以更新引用
 727 |                 console.log('重新导入预制体资源...');
 728 |                 const reimportResult = await this.reimportAssetWithAssetDB(savePath);
 729 | 
 730 |                 // 第七步:尝试将原始节点转换为预制体实例
 731 |                 console.log('尝试将原始节点转换为预制体实例...');
 732 |                 const convertResult = await this.convertNodeToPrefabInstance(nodeUuid, actualPrefabUuid, savePath);
 733 |                 
 734 |                 resolve({
 735 |                     success: true,
 736 |                     data: {
 737 |                         prefabUuid: actualPrefabUuid,
 738 |                         prefabPath: savePath,
 739 |                         nodeUuid: nodeUuid,
 740 |                         prefabName: prefabName,
 741 |                         convertedToPrefabInstance: convertResult.success,
 742 |                         createAssetResult: createResult,
 743 |                         updateResult: updateResult,
 744 |                         metaResult: metaResult,
 745 |                         reimportResult: reimportResult,
 746 |                         convertResult: convertResult,
 747 |                         message: convertResult.success ? '预制体创建并成功转换原始节点' : '预制体创建成功,但节点转换失败'
 748 |                     }
 749 |                 });
 750 | 
 751 |             } catch (error) {
 752 |                 console.error('创建预制体时发生错误:', error);
 753 |                 resolve({
 754 |                     success: false,
 755 |                     error: `创建预制体失败: ${error}`
 756 |                 });
 757 |             }
 758 |         });
 759 |     }
 760 | 
 761 |     private async createPrefab(args: any): Promise<ToolResponse> {
 762 |         return new Promise(async (resolve) => {
 763 |             try {
 764 |                 // 支持 prefabPath 和 savePath 两种参数名
 765 |                 const pathParam = args.prefabPath || args.savePath;
 766 |                 if (!pathParam) {
 767 |                     resolve({
 768 |                         success: false,
 769 |                         error: '缺少预制体路径参数。请提供 prefabPath 或 savePath。'
 770 |                     });
 771 |                     return;
 772 |                 }
 773 | 
 774 |                 const prefabName = args.prefabName || 'NewPrefab';
 775 |                 const fullPath = pathParam.endsWith('.prefab') ? 
 776 |                     pathParam : `${pathParam}/${prefabName}.prefab`;
 777 | 
 778 |                 const includeChildren = args.includeChildren !== false; // 默认为 true
 779 |                 const includeComponents = args.includeComponents !== false; // 默认为 true
 780 | 
 781 |                 // 优先使用新的 asset-db 方法创建预制体
 782 |                 console.log('使用新的 asset-db 方法创建预制体...');
 783 |                 const assetDbResult = await this.createPrefabWithAssetDB(
 784 |                     args.nodeUuid,
 785 |                     fullPath,
 786 |                     prefabName,
 787 |                     includeChildren,
 788 |                     includeComponents
 789 |                 );
 790 | 
 791 |                 if (assetDbResult.success) {
 792 |                     resolve(assetDbResult);
 793 |                     return;
 794 |                 }
 795 | 
 796 |                 // 如果 asset-db 方法失败,尝试使用Cocos Creator的原生预制体创建API
 797 |                 console.log('asset-db 方法失败,尝试原生API...');
 798 |                 const nativeResult = await this.createPrefabNative(args.nodeUuid, fullPath);
 799 |                 if (nativeResult.success) {
 800 |                     resolve(nativeResult);
 801 |                     return;
 802 |                 }
 803 | 
 804 |                 // 如果原生API失败,使用自定义实现
 805 |                 console.log('原生API失败,使用自定义实现...');
 806 |                 const customResult = await this.createPrefabCustom(args.nodeUuid, fullPath, prefabName);
 807 |                 resolve(customResult);
 808 | 
 809 |             } catch (error) {
 810 |                 resolve({
 811 |                     success: false,
 812 |                     error: `创建预制体时发生错误: ${error}`
 813 |                 });
 814 |             }
 815 |         });
 816 |     }
 817 | 
 818 |     private async createPrefabNative(nodeUuid: string, prefabPath: string): Promise<ToolResponse> {
 819 |         return new Promise((resolve) => {
 820 |             // 根据官方API文档,不存在直接的预制体创建API
 821 |             // 预制体创建需要手动在编辑器中完成
 822 |             resolve({
 823 |                 success: false,
 824 |                 error: '原生预制体创建API不存在',
 825 |                 instruction: '根据Cocos Creator官方API文档,预制体创建需要手动操作:\n1. 在场景中选择节点\n2. 将节点拖拽到资源管理器中\n3. 或右键节点选择"生成预制体"'
 826 |             });
 827 |         });
 828 |     }
 829 | 
 830 |     private async createPrefabCustom(nodeUuid: string, prefabPath: string, prefabName: string): Promise<ToolResponse> {
 831 |         return new Promise(async (resolve) => {
 832 |             try {
 833 |                 // 1. 获取源节点的完整数据
 834 |                 const nodeData = await this.getNodeData(nodeUuid);
 835 |                 if (!nodeData) {
 836 |                     resolve({
 837 |                         success: false,
 838 |                         error: `无法找到节点: ${nodeUuid}`
 839 |                     });
 840 |                     return;
 841 |                 }
 842 | 
 843 |                 // 2. 生成预制体UUID
 844 |                 const prefabUuid = this.generateUUID();
 845 | 
 846 |                 // 3. 创建预制体数据结构
 847 |                 const prefabData = this.createPrefabData(nodeData, prefabName, prefabUuid);
 848 | 
 849 |                 // 4. 基于官方格式创建预制体数据结构
 850 |                 console.log('=== 开始创建预制体 ===');
 851 |                 console.log('节点名称:', nodeData.name?.value || '未知');
 852 |                 console.log('节点UUID:', nodeData.uuid?.value || '未知');
 853 |                 console.log('预制体保存路径:', prefabPath);
 854 |                 console.log(`开始创建预制体,节点数据:`, nodeData);
 855 |                 const prefabJsonData = await this.createStandardPrefabContent(nodeData, prefabName, prefabUuid, true, true);
 856 | 
 857 |                 // 5. 创建标准meta文件数据
 858 |                 const standardMetaData = this.createStandardMetaData(prefabName, prefabUuid);
 859 | 
 860 |                 // 6. 保存预制体和meta文件
 861 |                 const saveResult = await this.savePrefabWithMeta(prefabPath, prefabJsonData, standardMetaData);
 862 | 
 863 |                 if (saveResult.success) {
 864 |                     // 保存成功后,将原始节点转换为预制体实例
 865 |                     const convertResult = await this.convertNodeToPrefabInstance(nodeUuid, prefabPath, prefabUuid);
 866 |                     
 867 |                     resolve({
 868 |                         success: true,
 869 |                         data: {
 870 |                             prefabUuid: prefabUuid,
 871 |                             prefabPath: prefabPath,
 872 |                             nodeUuid: nodeUuid,
 873 |                             prefabName: prefabName,
 874 |                             convertedToPrefabInstance: convertResult.success,
 875 |                             message: convertResult.success ? 
 876 |                                 '自定义预制体创建成功,原始节点已转换为预制体实例' : 
 877 |                                 '预制体创建成功,但节点转换失败'
 878 |                         }
 879 |                     });
 880 |                 } else {
 881 |                     resolve({
 882 |                         success: false,
 883 |                         error: saveResult.error || '保存预制体文件失败'
 884 |                     });
 885 |                 }
 886 | 
 887 |             } catch (error) {
 888 |                 resolve({
 889 |                     success: false,
 890 |                     error: `创建预制体时发生错误: ${error}`
 891 |                 });
 892 |             }
 893 |         });
 894 |     }
 895 | 
 896 |     private async getNodeData(nodeUuid: string): Promise<any> {
 897 |         return new Promise(async (resolve) => {
 898 |             try {
 899 |                 // 首先获取基本节点信息
 900 |                 const nodeInfo = await Editor.Message.request('scene', 'query-node', nodeUuid);
 901 |                 if (!nodeInfo) {
 902 |                     resolve(null);
 903 |                     return;
 904 |                 }
 905 | 
 906 |                 console.log(`获取节点 ${nodeUuid} 的基本信息成功`);
 907 |                 
 908 |                 // 使用query-node-tree获取包含子节点的完整结构
 909 |                 const nodeTree = await this.getNodeWithChildren(nodeUuid);
 910 |                 if (nodeTree) {
 911 |                     console.log(`获取节点 ${nodeUuid} 的完整树结构成功`);
 912 |                     resolve(nodeTree);
 913 |                 } else {
 914 |                     console.log(`使用基本节点信息`);
 915 |                     resolve(nodeInfo);
 916 |                 }
 917 |             } catch (error) {
 918 |                 console.warn(`获取节点数据失败 ${nodeUuid}:`, error);
 919 |                 resolve(null);
 920 |             }
 921 |         });
 922 |     }
 923 | 
 924 |     // 使用query-node-tree获取包含子节点的完整节点结构
 925 |     private async getNodeWithChildren(nodeUuid: string): Promise<any> {
 926 |         try {
 927 |             // 获取整个场景树
 928 |             const tree = await Editor.Message.request('scene', 'query-node-tree');
 929 |             if (!tree) {
 930 |                 return null;
 931 |             }
 932 | 
 933 |             // 在树中查找指定的节点
 934 |             const targetNode = this.findNodeInTree(tree, nodeUuid);
 935 |             if (targetNode) {
 936 |                 console.log(`在场景树中找到节点 ${nodeUuid},子节点数量: ${targetNode.children ? targetNode.children.length : 0}`);
 937 |                 
 938 |                 // 增强节点树,获取每个节点的正确组件信息
 939 |                 const enhancedTree = await this.enhanceTreeWithMCPComponents(targetNode);
 940 |                 return enhancedTree;
 941 |             }
 942 | 
 943 |             return null;
 944 |         } catch (error) {
 945 |             console.warn(`获取节点树结构失败 ${nodeUuid}:`, error);
 946 |             return null;
 947 |         }
 948 |     }
 949 | 
 950 |     // 在节点树中递归查找指定UUID的节点
 951 |     private findNodeInTree(node: any, targetUuid: string): any {
 952 |         if (!node) return null;
 953 |         
 954 |         // 检查当前节点
 955 |         if (node.uuid === targetUuid || node.value?.uuid === targetUuid) {
 956 |             return node;
 957 |         }
 958 | 
 959 |         // 递归检查子节点
 960 |         if (node.children && Array.isArray(node.children)) {
 961 |             for (const child of node.children) {
 962 |                 const found = this.findNodeInTree(child, targetUuid);
 963 |                 if (found) {
 964 |                     return found;
 965 |                 }
 966 |             }
 967 |         }
 968 | 
 969 |         return null;
 970 |     }
 971 | 
 972 |     /**
 973 |      * 使用MCP接口增强节点树,获取正确的组件信息
 974 |      */
 975 |     private async enhanceTreeWithMCPComponents(node: any): Promise<any> {
 976 |         if (!node || !node.uuid) {
 977 |             return node;
 978 |         }
 979 | 
 980 |         try {
 981 |             // 使用MCP接口获取节点的组件信息
 982 |             const response = await fetch('http://localhost:8585/mcp', {
 983 |                 method: 'POST',
 984 |                 headers: { 'Content-Type': 'application/json' },
 985 |                 body: JSON.stringify({
 986 |                     "jsonrpc": "2.0",
 987 |                     "method": "tools/call",
 988 |                     "params": {
 989 |                         "name": "component_get_components",
 990 |                         "arguments": {
 991 |                             "nodeUuid": node.uuid
 992 |                         }
 993 |                     },
 994 |                     "id": Date.now()
 995 |                 })
 996 |             });
 997 |             
 998 |             const mcpResult = await response.json();
 999 |             if (mcpResult.result?.content?.[0]?.text) {
1000 |                 const componentData = JSON.parse(mcpResult.result.content[0].text);
1001 |                 if (componentData.success && componentData.data.components) {
1002 |                     // 更新节点的组件信息为MCP返回的正确数据
1003 |                     node.components = componentData.data.components;
1004 |                     console.log(`节点 ${node.uuid} 获取到 ${componentData.data.components.length} 个组件,包含脚本组件的正确类型`);
1005 |                 }
1006 |             }
1007 |         } catch (error) {
1008 |             console.warn(`获取节点 ${node.uuid} 的MCP组件信息失败:`, error);
1009 |         }
1010 | 
1011 |         // 递归处理子节点
1012 |         if (node.children && Array.isArray(node.children)) {
1013 |             for (let i = 0; i < node.children.length; i++) {
1014 |                 node.children[i] = await this.enhanceTreeWithMCPComponents(node.children[i]);
1015 |             }
1016 |         }
1017 | 
1018 |         return node;
1019 |     }
1020 | 
1021 |     private async buildBasicNodeInfo(nodeUuid: string): Promise<any> {
1022 |         return new Promise((resolve) => {
1023 |             // 构建基本的节点信息
1024 |             Editor.Message.request('scene', 'query-node', nodeUuid).then((nodeInfo: any) => {
1025 |                 if (!nodeInfo) {
1026 |                     resolve(null);
1027 |                     return;
1028 |                 }
1029 | 
1030 |                 // 简化版本:只返回基本节点信息,不获取子节点和组件
1031 |                 // 这些信息将在后续的预制体处理中根据需要添加
1032 |                 const basicInfo = {
1033 |                     ...nodeInfo,
1034 |                     children: [],
1035 |                     components: []
1036 |                 };
1037 |                 resolve(basicInfo);
1038 |             }).catch(() => {
1039 |                 resolve(null);
1040 |             });
1041 |         });
1042 |     }
1043 | 
1044 |     // 验证节点数据是否有效
1045 |     private isValidNodeData(nodeData: any): boolean {
1046 |         if (!nodeData) return false;
1047 |         if (typeof nodeData !== 'object') return false;
1048 |         
1049 |         // 检查基本属性 - 适配query-node-tree的数据格式
1050 |         return nodeData.hasOwnProperty('uuid') || 
1051 |                nodeData.hasOwnProperty('name') || 
1052 |                nodeData.hasOwnProperty('__type__') ||
1053 |                (nodeData.value && (
1054 |                    nodeData.value.hasOwnProperty('uuid') ||
1055 |                    nodeData.value.hasOwnProperty('name') ||
1056 |                    nodeData.value.hasOwnProperty('__type__')
1057 |                ));
1058 |     }
1059 | 
1060 |     // 提取子节点UUID的统一方法
1061 |     private extractChildUuid(childRef: any): string | null {
1062 |         if (!childRef) return null;
1063 |         
1064 |         // 方法1: 直接字符串
1065 |         if (typeof childRef === 'string') {
1066 |             return childRef;
1067 |         }
1068 |         
1069 |         // 方法2: value属性包含字符串
1070 |         if (childRef.value && typeof childRef.value === 'string') {
1071 |             return childRef.value;
1072 |         }
1073 |         
1074 |         // 方法3: value.uuid属性
1075 |         if (childRef.value && childRef.value.uuid) {
1076 |             return childRef.value.uuid;
1077 |         }
1078 |         
1079 |         // 方法4: 直接uuid属性
1080 |         if (childRef.uuid) {
1081 |             return childRef.uuid;
1082 |         }
1083 |         
1084 |         // 方法5: __id__引用 - 这种情况需要特殊处理
1085 |         if (childRef.__id__ !== undefined) {
1086 |             console.log(`发现__id__引用: ${childRef.__id__},可能需要从数据结构中查找`);
1087 |             return null; // 暂时返回null,后续可以添加引用解析逻辑
1088 |         }
1089 |         
1090 |         console.warn('无法提取子节点UUID:', JSON.stringify(childRef));
1091 |         return null;
1092 |     }
1093 | 
1094 |     // 获取需要处理的子节点数据
1095 |     private getChildrenToProcess(nodeData: any): any[] {
1096 |         const children: any[] = [];
1097 |         
1098 |         // 方法1: 直接从children数组获取(从query-node-tree返回的数据)
1099 |         if (nodeData.children && Array.isArray(nodeData.children)) {
1100 |             console.log(`从children数组获取子节点,数量: ${nodeData.children.length}`);
1101 |             for (const child of nodeData.children) {
1102 |                 // query-node-tree返回的子节点通常已经是完整的数据结构
1103 |                 if (this.isValidNodeData(child)) {
1104 |                     children.push(child);
1105 |                     console.log(`添加子节点: ${child.name || child.value?.name || '未知'}`);
1106 |                 } else {
1107 |                     console.log('子节点数据无效:', JSON.stringify(child, null, 2));
1108 |                 }
1109 |             }
1110 |         } else {
1111 |             console.log('节点没有子节点或children数组为空');
1112 |         }
1113 |         
1114 |         return children;
1115 |     }
1116 | 
1117 |     private generateUUID(): string {
1118 |         // 生成符合Cocos Creator格式的UUID
1119 |         const chars = '0123456789abcdef';
1120 |         let uuid = '';
1121 |         for (let i = 0; i < 32; i++) {
1122 |             if (i === 8 || i === 12 || i === 16 || i === 20) {
1123 |                 uuid += '-';
1124 |             }
1125 |             uuid += chars[Math.floor(Math.random() * chars.length)];
1126 |         }
1127 |         return uuid;
1128 |     }
1129 | 
1130 |     private createPrefabData(nodeData: any, prefabName: string, prefabUuid: string): any[] {
1131 |         // 创建标准的预制体数据结构
1132 |         const prefabAsset = {
1133 |             "__type__": "cc.Prefab",
1134 |             "_name": prefabName,
1135 |             "_objFlags": 0,
1136 |             "__editorExtras__": {},
1137 |             "_native": "",
1138 |             "data": {
1139 |                 "__id__": 1
1140 |             },
1141 |             "optimizationPolicy": 0,
1142 |             "persistent": false
1143 |         };
1144 | 
1145 |         // 处理节点数据,确保符合预制体格式
1146 |         const processedNodeData = this.processNodeForPrefab(nodeData, prefabUuid);
1147 | 
1148 |         return [prefabAsset, ...processedNodeData];
1149 |     }
1150 | 
1151 |     private processNodeForPrefab(nodeData: any, prefabUuid: string): any[] {
1152 |         // 处理节点数据以符合预制体格式
1153 |         const processedData: any[] = [];
1154 |         let idCounter = 1;
1155 | 
1156 |         // 递归处理节点和组件
1157 |         const processNode = (node: any, parentId: number = 0): number => {
1158 |             const nodeId = idCounter++;
1159 |             
1160 |             // 创建节点对象
1161 |             const processedNode = {
1162 |                 "__type__": "cc.Node",
1163 |                 "_name": node.name || "Node",
1164 |                 "_objFlags": 0,
1165 |                 "__editorExtras__": {},
1166 |                 "_parent": parentId > 0 ? { "__id__": parentId } : null,
1167 |                 "_children": node.children ? node.children.map(() => ({ "__id__": idCounter++ })) : [],
1168 |                 "_active": node.active !== false,
1169 |                 "_components": node.components ? node.components.map(() => ({ "__id__": idCounter++ })) : [],
1170 |                 "_prefab": {
1171 |                     "__id__": idCounter++
1172 |                 },
1173 |                 "_lpos": {
1174 |                     "__type__": "cc.Vec3",
1175 |                     "x": 0,
1176 |                     "y": 0,
1177 |                     "z": 0
1178 |                 },
1179 |                 "_lrot": {
1180 |                     "__type__": "cc.Quat",
1181 |                     "x": 0,
1182 |                     "y": 0,
1183 |                     "z": 0,
1184 |                     "w": 1
1185 |                 },
1186 |                 "_lscale": {
1187 |                     "__type__": "cc.Vec3",
1188 |                     "x": 1,
1189 |                     "y": 1,
1190 |                     "z": 1
1191 |                 },
1192 |                 "_mobility": 0,
1193 |                 "_layer": 1073741824,
1194 |                 "_euler": {
1195 |                     "__type__": "cc.Vec3",
1196 |                     "x": 0,
1197 |                     "y": 0,
1198 |                     "z": 0
1199 |                 },
1200 |                 "_id": ""
1201 |             };
1202 | 
1203 |             processedData.push(processedNode);
1204 | 
1205 |             // 处理组件
1206 |             if (node.components) {
1207 |                 node.components.forEach((component: any) => {
1208 |                     const componentId = idCounter++;
1209 |                     const processedComponents = this.processComponentForPrefab(component, componentId);
1210 |                     processedData.push(...processedComponents);
1211 |                 });
1212 |             }
1213 | 
1214 |             // 处理子节点
1215 |             if (node.children) {
1216 |                 node.children.forEach((child: any) => {
1217 |                     processNode(child, nodeId);
1218 |                 });
1219 |             }
1220 | 
1221 |             return nodeId;
1222 |         };
1223 | 
1224 |         processNode(nodeData);
1225 |         return processedData;
1226 |     }
1227 | 
1228 |     private processComponentForPrefab(component: any, componentId: number): any[] {
1229 |         // 处理组件数据以符合预制体格式
1230 |         const processedComponent = {
1231 |             "__type__": component.type || "cc.Component",
1232 |             "_name": "",
1233 |             "_objFlags": 0,
1234 |             "__editorExtras__": {},
1235 |             "node": {
1236 |                 "__id__": componentId - 1
1237 |             },
1238 |             "_enabled": component.enabled !== false,
1239 |             "__prefab": {
1240 |                 "__id__": componentId + 1
1241 |             },
1242 |             ...component.properties
1243 |         };
1244 | 
1245 |         // 添加组件特定的预制体信息
1246 |         const compPrefabInfo = {
1247 |             "__type__": "cc.CompPrefabInfo",
1248 |             "fileId": this.generateFileId()
1249 |         };
1250 | 
1251 |         return [processedComponent, compPrefabInfo];
1252 |     }
1253 | 
1254 |     private generateFileId(): string {
1255 |         // 生成文件ID(简化版本)
1256 |         const chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/';
1257 |         let fileId = '';
1258 |         for (let i = 0; i < 22; i++) {
1259 |             fileId += chars[Math.floor(Math.random() * chars.length)];
1260 |         }
1261 |         return fileId;
1262 |     }
1263 | 
1264 |     private createMetaData(prefabName: string, prefabUuid: string): any {
1265 |         return {
1266 |             "ver": "1.1.50",
1267 |             "importer": "prefab",
1268 |             "imported": true,
1269 |             "uuid": prefabUuid,
1270 |             "files": [
1271 |                 ".json"
1272 |             ],
1273 |             "subMetas": {},
1274 |             "userData": {
1275 |                 "syncNodeName": prefabName
1276 |             }
1277 |         };
1278 |     }
1279 | 
1280 |     private async savePrefabFiles(prefabPath: string, prefabData: any[], metaData: any): Promise<{ success: boolean; error?: string }> {
1281 |         return new Promise((resolve) => {
1282 |             try {
1283 |                 // 使用Editor API保存预制体文件
1284 |                 const prefabContent = JSON.stringify(prefabData, null, 2);
1285 |                 const metaContent = JSON.stringify(metaData, null, 2);
1286 |                 
1287 |                 // 尝试使用更可靠的保存方法
1288 |                 this.saveAssetFile(prefabPath, prefabContent).then(() => {
1289 |                     // 再创建meta文件
1290 |                     const metaPath = `${prefabPath}.meta`;
1291 |                     return this.saveAssetFile(metaPath, metaContent);
1292 |                 }).then(() => {
1293 |                     resolve({ success: true });
1294 |                 }).catch((error: any) => {
1295 |                     resolve({ success: false, error: error.message || '保存预制体文件失败' });
1296 |                 });
1297 |             } catch (error) {
1298 |                 resolve({ success: false, error: `保存文件时发生错误: ${error}` });
1299 |             }
1300 |         });
1301 |     }
1302 | 
1303 |     private async saveAssetFile(filePath: string, content: string): Promise<void> {
1304 |         return new Promise((resolve, reject) => {
1305 |             // 尝试多种保存方法
1306 |             const saveMethods = [
1307 |                 () => Editor.Message.request('asset-db', 'create-asset', filePath, content),
1308 |                 () => Editor.Message.request('asset-db', 'save-asset', filePath, content),
1309 |                 () => Editor.Message.request('asset-db', 'write-asset', filePath, content)
1310 |             ];
1311 | 
1312 |             const trySave = (index: number) => {
1313 |                 if (index >= saveMethods.length) {
1314 |                     reject(new Error('所有保存方法都失败了'));
1315 |                     return;
1316 |                 }
1317 | 
1318 |                 saveMethods[index]().then(() => {
1319 |                     resolve();
1320 |                 }).catch(() => {
1321 |                     trySave(index + 1);
1322 |                 });
1323 |             };
1324 | 
1325 |             trySave(0);
1326 |         });
1327 |     }
1328 | 
1329 |     private async updatePrefab(prefabPath: string, nodeUuid: string): Promise<ToolResponse> {
1330 |         return new Promise((resolve) => {
1331 |             Editor.Message.request('asset-db', 'query-asset-info', prefabPath).then((assetInfo: any) => {
1332 |                 if (!assetInfo) {
1333 |                     throw new Error('Prefab not found');
1334 |                 }
1335 | 
1336 |                 return Editor.Message.request('scene', 'apply-prefab', {
1337 |                     node: nodeUuid,
1338 |                     prefab: assetInfo.uuid
1339 |                 });
1340 |             }).then(() => {
1341 |                 resolve({
1342 |                     success: true,
1343 |                     message: 'Prefab updated successfully'
1344 |                 });
1345 |             }).catch((err: Error) => {
1346 |                 resolve({ success: false, error: err.message });
1347 |             });
1348 |         });
1349 |     }
1350 | 
1351 |     private async revertPrefab(nodeUuid: string): Promise<ToolResponse> {
1352 |         return new Promise((resolve) => {
1353 |             Editor.Message.request('scene', 'revert-prefab', {
1354 |                 node: nodeUuid
1355 |             }).then(() => {
1356 |                 resolve({
1357 |                     success: true,
1358 |                     message: 'Prefab instance reverted successfully'
1359 |                 });
1360 |             }).catch((err: Error) => {
1361 |                 resolve({ success: false, error: err.message });
1362 |             });
1363 |         });
1364 |     }
1365 | 
1366 |     private async getPrefabInfo(prefabPath: string): Promise<ToolResponse> {
1367 |         return new Promise((resolve) => {
1368 |             Editor.Message.request('asset-db', 'query-asset-info', prefabPath).then((assetInfo: any) => {
1369 |                 if (!assetInfo) {
1370 |                     throw new Error('Prefab not found');
1371 |                 }
1372 | 
1373 |                 return Editor.Message.request('asset-db', 'query-asset-meta', assetInfo.uuid);
1374 |             }).then((metaInfo: any) => {
1375 |                 const info: PrefabInfo = {
1376 |                     name: metaInfo.name,
1377 |                     uuid: metaInfo.uuid,
1378 |                     path: prefabPath,
1379 |                     folder: prefabPath.substring(0, prefabPath.lastIndexOf('/')),
1380 |                     createTime: metaInfo.createTime,
1381 |                     modifyTime: metaInfo.modifyTime,
1382 |                     dependencies: metaInfo.depends || []
1383 |                 };
1384 |                 resolve({ success: true, data: info });
1385 |             }).catch((err: Error) => {
1386 |                 resolve({ success: false, error: err.message });
1387 |             });
1388 |         });
1389 |     }
1390 | 
1391 |     private async createPrefabFromNode(args: any): Promise<ToolResponse> {
1392 |         // 从 prefabPath 提取名称
1393 |         const prefabPath = args.prefabPath;
1394 |         const prefabName = prefabPath.split('/').pop()?.replace('.prefab', '') || 'NewPrefab';
1395 |         
1396 |         // 调用原来的 createPrefab 方法
1397 |         return await this.createPrefab({
1398 |             nodeUuid: args.nodeUuid,
1399 |             savePath: prefabPath,
1400 |             prefabName: prefabName
1401 |         });
1402 |     }
1403 | 
1404 |     private async validatePrefab(prefabPath: string): Promise<ToolResponse> {
1405 |         return new Promise((resolve) => {
1406 |             try {
1407 |                 // 读取预制体文件内容
1408 |                 Editor.Message.request('asset-db', 'query-asset-info', prefabPath).then((assetInfo: any) => {
1409 |                     if (!assetInfo) {
1410 |                         resolve({
1411 |                             success: false,
1412 |                             error: '预制体文件不存在'
1413 |                         });
1414 |                         return;
1415 |                     }
1416 | 
1417 |                     // 验证预制体格式
1418 |                     Editor.Message.request('asset-db', 'read-asset', prefabPath).then((content: string) => {
1419 |                         try {
1420 |                             const prefabData = JSON.parse(content);
1421 |                             const validationResult = this.validatePrefabFormat(prefabData);
1422 |                             
1423 |                             resolve({
1424 |                                 success: true,
1425 |                                 data: {
1426 |                                     isValid: validationResult.isValid,
1427 |                                     issues: validationResult.issues,
1428 |                                     nodeCount: validationResult.nodeCount,
1429 |                                     componentCount: validationResult.componentCount,
1430 |                                     message: validationResult.isValid ? '预制体格式有效' : '预制体格式存在问题'
1431 |                                 }
1432 |                             });
1433 |                         } catch (parseError) {
1434 |                             resolve({
1435 |                                 success: false,
1436 |                                 error: '预制体文件格式错误,无法解析JSON'
1437 |                             });
1438 |                         }
1439 |                     }).catch((error: any) => {
1440 |                         resolve({
1441 |                             success: false,
1442 |                             error: `读取预制体文件失败: ${error.message}`
1443 |                         });
1444 |                     });
1445 |                 }).catch((error: any) => {
1446 |                     resolve({
1447 |                         success: false,
1448 |                         error: `查询预制体信息失败: ${error.message}`
1449 |                     });
1450 |                 });
1451 |             } catch (error) {
1452 |                 resolve({
1453 |                     success: false,
1454 |                     error: `验证预制体时发生错误: ${error}`
1455 |                 });
1456 |             }
1457 |         });
1458 |     }
1459 | 
1460 |     private validatePrefabFormat(prefabData: any): { isValid: boolean; issues: string[]; nodeCount: number; componentCount: number } {
1461 |         const issues: string[] = [];
1462 |         let nodeCount = 0;
1463 |         let componentCount = 0;
1464 | 
1465 |         // 检查基本结构
1466 |         if (!Array.isArray(prefabData)) {
1467 |             issues.push('预制体数据必须是数组格式');
1468 |             return { isValid: false, issues, nodeCount, componentCount };
1469 |         }
1470 | 
1471 |         if (prefabData.length === 0) {
1472 |             issues.push('预制体数据为空');
1473 |             return { isValid: false, issues, nodeCount, componentCount };
1474 |         }
1475 | 
1476 |         // 检查第一个元素是否为预制体资产
1477 |         const firstElement = prefabData[0];
1478 |         if (!firstElement || firstElement.__type__ !== 'cc.Prefab') {
1479 |             issues.push('第一个元素必须是cc.Prefab类型');
1480 |         }
1481 | 
1482 |         // 统计节点和组件
1483 |         prefabData.forEach((item: any, index: number) => {
1484 |             if (item.__type__ === 'cc.Node') {
1485 |                 nodeCount++;
1486 |             } else if (item.__type__ && item.__type__.includes('cc.')) {
1487 |                 componentCount++;
1488 |             }
1489 |         });
1490 | 
1491 |         // 检查必要的字段
1492 |         if (nodeCount === 0) {
1493 |             issues.push('预制体必须包含至少一个节点');
1494 |         }
1495 | 
1496 |         return {
1497 |             isValid: issues.length === 0,
1498 |             issues,
1499 |             nodeCount,
1500 |             componentCount
1501 |         };
1502 |     }
1503 | 
1504 |     private async duplicatePrefab(args: any): Promise<ToolResponse> {
1505 |         return new Promise(async (resolve) => {
1506 |             try {
1507 |                 const { sourcePrefabPath, targetPrefabPath, newPrefabName } = args;
1508 |                 
1509 |                 // 读取源预制体
1510 |                 const sourceInfo = await this.getPrefabInfo(sourcePrefabPath);
1511 |                 if (!sourceInfo.success) {
1512 |                     resolve({
1513 |                         success: false,
1514 |                         error: `无法读取源预制体: ${sourceInfo.error}`
1515 |                     });
1516 |                     return;
1517 |                 }
1518 | 
1519 |                 // 读取源预制体内容
1520 |                 const sourceContent = await this.readPrefabContent(sourcePrefabPath);
1521 |                 if (!sourceContent.success) {
1522 |                     resolve({
1523 |                         success: false,
1524 |                         error: `无法读取源预制体内容: ${sourceContent.error}`
1525 |                     });
1526 |                     return;
1527 |                 }
1528 | 
1529 |                 // 生成新的UUID
1530 |                 const newUuid = this.generateUUID();
1531 |                 
1532 |                 // 修改预制体数据
1533 |                 const modifiedData = this.modifyPrefabForDuplication(sourceContent.data, newPrefabName, newUuid);
1534 |                 
1535 |                 // 创建新的meta数据
1536 |                 const newMetaData = this.createMetaData(newPrefabName || 'DuplicatedPrefab', newUuid);
1537 |                 
1538 |                 // 预制体复制功能暂时禁用,因为涉及复杂的序列化格式
1539 |                 resolve({
1540 |                     success: false,
1541 |                     error: '预制体复制功能暂时不可用',
1542 |                     instruction: '请在 Cocos Creator 编辑器中手动复制预制体:\n1. 在资源管理器中选择要复制的预制体\n2. 右键选择复制\n3. 在目标位置粘贴'
1543 |                 });
1544 | 
1545 |             } catch (error) {
1546 |                 resolve({
1547 |                     success: false,
1548 |                     error: `复制预制体时发生错误: ${error}`
1549 |                 });
1550 |             }
1551 |         });
1552 |     }
1553 | 
1554 |     private async readPrefabContent(prefabPath: string): Promise<{ success: boolean; data?: any; error?: string }> {
1555 |         return new Promise((resolve) => {
1556 |             Editor.Message.request('asset-db', 'read-asset', prefabPath).then((content: string) => {
1557 |                 try {
1558 |                     const prefabData = JSON.parse(content);
1559 |                     resolve({ success: true, data: prefabData });
1560 |                 } catch (parseError) {
1561 |                     resolve({ success: false, error: '预制体文件格式错误' });
1562 |                 }
1563 |             }).catch((error: any) => {
1564 |                 resolve({ success: false, error: error.message || '读取预制体文件失败' });
1565 |             });
1566 |         });
1567 |     }
1568 | 
1569 |     private modifyPrefabForDuplication(prefabData: any[], newName: string, newUuid: string): any[] {
1570 |         // 修改预制体数据以创建副本
1571 |         const modifiedData = [...prefabData];
1572 |         
1573 |         // 修改第一个元素(预制体资产)
1574 |         if (modifiedData[0] && modifiedData[0].__type__ === 'cc.Prefab') {
1575 |             modifiedData[0]._name = newName || 'DuplicatedPrefab';
1576 |         }
1577 | 
1578 |         // 更新所有UUID引用(简化版本)
1579 |         // 在实际应用中,可能需要更复杂的UUID映射处理
1580 |         
1581 |         return modifiedData;
1582 |     }
1583 | 
1584 |     /**
1585 |      * 使用 asset-db API 创建资源文件
1586 |      */
1587 |     private async createAssetWithAssetDB(assetPath: string, content: string): Promise<{ success: boolean; data?: any; error?: string }> {
1588 |         return new Promise((resolve) => {
1589 |             Editor.Message.request('asset-db', 'create-asset', assetPath, content, {
1590 |                 overwrite: true,
1591 |                 rename: false
1592 |             }).then((assetInfo: any) => {
1593 |                 console.log('创建资源文件成功:', assetInfo);
1594 |                 resolve({ success: true, data: assetInfo });
1595 |             }).catch((error: any) => {
1596 |                 console.error('创建资源文件失败:', error);
1597 |                 resolve({ success: false, error: error.message || '创建资源文件失败' });
1598 |             });
1599 |         });
1600 |     }
1601 | 
1602 |     /**
1603 |      * 使用 asset-db API 创建 meta 文件
1604 |      */
1605 |     private async createMetaWithAssetDB(assetPath: string, metaContent: any): Promise<{ success: boolean; data?: any; error?: string }> {
1606 |         return new Promise((resolve) => {
1607 |             const metaContentString = JSON.stringify(metaContent, null, 2);
1608 |             Editor.Message.request('asset-db', 'save-asset-meta', assetPath, metaContentString).then((assetInfo: any) => {
1609 |                 console.log('创建meta文件成功:', assetInfo);
1610 |                 resolve({ success: true, data: assetInfo });
1611 |             }).catch((error: any) => {
1612 |                 console.error('创建meta文件失败:', error);
1613 |                 resolve({ success: false, error: error.message || '创建meta文件失败' });
1614 |             });
1615 |         });
1616 |     }
1617 | 
1618 |     /**
1619 |      * 使用 asset-db API 重新导入资源
1620 |      */
1621 |     private async reimportAssetWithAssetDB(assetPath: string): Promise<{ success: boolean; data?: any; error?: string }> {
1622 |         return new Promise((resolve) => {
1623 |             Editor.Message.request('asset-db', 'reimport-asset', assetPath).then((result: any) => {
1624 |                 console.log('重新导入资源成功:', result);
1625 |                 resolve({ success: true, data: result });
1626 |             }).catch((error: any) => {
1627 |                 console.error('重新导入资源失败:', error);
1628 |                 resolve({ success: false, error: error.message || '重新导入资源失败' });
1629 |             });
1630 |         });
1631 |     }
1632 | 
1633 |     /**
1634 |      * 使用 asset-db API 更新资源文件内容
1635 |      */
1636 |     private async updateAssetWithAssetDB(assetPath: string, content: string): Promise<{ success: boolean; data?: any; error?: string }> {
1637 |         return new Promise((resolve) => {
1638 |             Editor.Message.request('asset-db', 'save-asset', assetPath, content).then((result: any) => {
1639 |                 console.log('更新资源文件成功:', result);
1640 |                 resolve({ success: true, data: result });
1641 |             }).catch((error: any) => {
1642 |                 console.error('更新资源文件失败:', error);
1643 |                 resolve({ success: false, error: error.message || '更新资源文件失败' });
1644 |             });
1645 |         });
1646 |     }
1647 | 
1648 |     /**
1649 |      * 创建符合 Cocos Creator 标准的预制体内容
1650 |      * 完整实现递归节点树处理,匹配引擎标准格式
1651 |      */
1652 |     private async createStandardPrefabContent(nodeData: any, prefabName: string, prefabUuid: string, includeChildren: boolean, includeComponents: boolean): Promise<any[]> {
1653 |         console.log('开始创建引擎标准预制体内容...');
1654 |         
1655 |         const prefabData: any[] = [];
1656 |         let currentId = 0;
1657 | 
1658 |         // 1. 创建预制体资产对象 (index 0)
1659 |         const prefabAsset = {
1660 |             "__type__": "cc.Prefab",
1661 |             "_name": prefabName || "", // 确保预制体名称不为空
1662 |             "_objFlags": 0,
1663 |             "__editorExtras__": {},
1664 |             "_native": "",
1665 |             "data": {
1666 |                 "__id__": 1
1667 |             },
1668 |             "optimizationPolicy": 0,
1669 |             "persistent": false
1670 |         };
1671 |         prefabData.push(prefabAsset);
1672 |         currentId++;
1673 | 
1674 |         // 2. 递归创建完整的节点树结构
1675 |         const context = {
1676 |             prefabData,
1677 |             currentId: currentId + 1, // 根节点占用索引1,子节点从索引2开始
1678 |             prefabAssetIndex: 0,
1679 |             nodeFileIds: new Map<string, string>(), // 存储节点ID到fileId的映射
1680 |             nodeUuidToIndex: new Map<string, number>(), // 存储节点UUID到索引的映射
1681 |             componentUuidToIndex: new Map<string, number>() // 存储组件UUID到索引的映射
1682 |         };
1683 | 
1684 |         // 创建根节点和整个节点树 - 注意:根节点的父节点应该是null,不是预制体对象
1685 |         await this.createCompleteNodeTree(nodeData, null, 1, context, includeChildren, includeComponents, prefabName);
1686 | 
1687 |         console.log(`预制体内容创建完成,总共 ${prefabData.length} 个对象`);
1688 |         console.log('节点fileId映射:', Array.from(context.nodeFileIds.entries()));
1689 |         
1690 |         return prefabData;
1691 |     }
1692 | 
1693 |     /**
1694 |      * 递归创建完整的节点树,包括所有子节点和对应的PrefabInfo
1695 |      */
1696 |     private async createCompleteNodeTree(
1697 |         nodeData: any, 
1698 |         parentNodeIndex: number | null, 
1699 |         nodeIndex: number,
1700 |         context: { 
1701 |             prefabData: any[], 
1702 |             currentId: number, 
1703 |             prefabAssetIndex: number, 
1704 |             nodeFileIds: Map<string, string>,
1705 |             nodeUuidToIndex: Map<string, number>,
1706 |             componentUuidToIndex: Map<string, number>
1707 |         },
1708 |         includeChildren: boolean,
1709 |         includeComponents: boolean,
1710 |         nodeName?: string
1711 |     ): Promise<void> {
1712 |         const { prefabData } = context;
1713 |         
1714 |         // 创建节点对象
1715 |         const node = this.createEngineStandardNode(nodeData, parentNodeIndex, nodeName);
1716 |         
1717 |         // 确保节点在指定的索引位置
1718 |         while (prefabData.length <= nodeIndex) {
1719 |             prefabData.push(null);
1720 |         }
1721 |         console.log(`设置节点到索引 ${nodeIndex}: ${node._name}, _parent:`, node._parent, `_children count: ${node._children.length}`);
1722 |         prefabData[nodeIndex] = node;
1723 |         
1724 |         // 为当前节点生成fileId并记录UUID到索引的映射
1725 |         const nodeUuid = this.extractNodeUuid(nodeData);
1726 |         const fileId = nodeUuid || this.generateFileId();
1727 |         context.nodeFileIds.set(nodeIndex.toString(), fileId);
1728 |         
1729 |         // 记录节点UUID到索引的映射
1730 |         if (nodeUuid) {
1731 |             context.nodeUuidToIndex.set(nodeUuid, nodeIndex);
1732 |             console.log(`记录节点UUID映射: ${nodeUuid} -> ${nodeIndex}`);
1733 |         }
1734 | 
1735 |         // 先处理子节点(保持与手动创建的索引顺序一致)
1736 |         const childrenToProcess = this.getChildrenToProcess(nodeData);
1737 |         if (includeChildren && childrenToProcess.length > 0) {
1738 |             console.log(`处理节点 ${node._name} 的 ${childrenToProcess.length} 个子节点`);
1739 |             
1740 |             // 为每个子节点分配索引
1741 |             const childIndices: number[] = [];
1742 |             console.log(`准备为 ${childrenToProcess.length} 个子节点分配索引,当前ID: ${context.currentId}`);
1743 |             for (let i = 0; i < childrenToProcess.length; i++) {
1744 |                 console.log(`处理第 ${i+1} 个子节点,当前currentId: ${context.currentId}`);
1745 |                 const childIndex = context.currentId++;
1746 |                 childIndices.push(childIndex);
1747 |                 node._children.push({ "__id__": childIndex });
1748 |                 console.log(`✅ 添加子节点引用到 ${node._name}: {__id__: ${childIndex}}`);
1749 |             }
1750 |             console.log(`✅ 节点 ${node._name} 最终的子节点数组:`, node._children);
1751 | 
1752 |             // 递归创建子节点
1753 |             for (let i = 0; i < childrenToProcess.length; i++) {
1754 |                 const childData = childrenToProcess[i];
1755 |                 const childIndex = childIndices[i];
1756 |                 await this.createCompleteNodeTree(
1757 |                     childData, 
1758 |                     nodeIndex, 
1759 |                     childIndex, 
1760 |                     context,
1761 |                     includeChildren,
1762 |                     includeComponents,
1763 |                     childData.name || `Child${i+1}`
1764 |                 );
1765 |             }
1766 |         }
1767 | 
1768 |         // 然后处理组件
1769 |         if (includeComponents && nodeData.components && Array.isArray(nodeData.components)) {
1770 |             console.log(`处理节点 ${node._name} 的 ${nodeData.components.length} 个组件`);
1771 |             
1772 |             const componentIndices: number[] = [];
1773 |             for (const component of nodeData.components) {
1774 |                 const componentIndex = context.currentId++;
1775 |                 componentIndices.push(componentIndex);
1776 |                 node._components.push({ "__id__": componentIndex });
1777 |                 
1778 |                 // 记录组件UUID到索引的映射
1779 |                 const componentUuid = component.uuid || (component.value && component.value.uuid);
1780 |                 if (componentUuid) {
1781 |                     context.componentUuidToIndex.set(componentUuid, componentIndex);
1782 |                     console.log(`记录组件UUID映射: ${componentUuid} -> ${componentIndex}`);
1783 |                 }
1784 |                 
1785 |                 // 创建组件对象,传入context以处理引用
1786 |                 const componentObj = this.createComponentObject(component, nodeIndex, context);
1787 |                 prefabData[componentIndex] = componentObj;
1788 |                 
1789 |                 // 为组件创建 CompPrefabInfo
1790 |                 const compPrefabInfoIndex = context.currentId++;
1791 |                 prefabData[compPrefabInfoIndex] = {
1792 |                     "__type__": "cc.CompPrefabInfo",
1793 |                     "fileId": this.generateFileId()
1794 |                 };
1795 |                 
1796 |                 // 如果组件对象有 __prefab 属性,设置引用
1797 |                 if (componentObj && typeof componentObj === 'object') {
1798 |                     componentObj.__prefab = { "__id__": compPrefabInfoIndex };
1799 |                 }
1800 |             }
1801 |             
1802 |             console.log(`✅ 节点 ${node._name} 添加了 ${componentIndices.length} 个组件`);
1803 |         }
1804 | 
1805 | 
1806 |         // 为当前节点创建PrefabInfo
1807 |         const prefabInfoIndex = context.currentId++;
1808 |         node._prefab = { "__id__": prefabInfoIndex };
1809 |         
1810 |         const prefabInfo: any = {
1811 |             "__type__": "cc.PrefabInfo",
1812 |             "root": { "__id__": 1 },
1813 |             "asset": { "__id__": context.prefabAssetIndex },
1814 |             "fileId": fileId,
1815 |             "targetOverrides": null,
1816 |             "nestedPrefabInstanceRoots": null
1817 |         };
1818 |         
1819 |         // 根节点的特殊处理
1820 |         if (nodeIndex === 1) {
1821 |             // 根节点没有instance,但可能有targetOverrides
1822 |             prefabInfo.instance = null;
1823 |         } else {
1824 |             // 子节点通常有instance为null
1825 |             prefabInfo.instance = null;
1826 |         }
1827 |         
1828 |         prefabData[prefabInfoIndex] = prefabInfo;
1829 |         context.currentId = prefabInfoIndex + 1;
1830 |     }
1831 | 
1832 |     /**
1833 |      * 将UUID转换为Cocos Creator的压缩格式
1834 |      * 基于真实Cocos Creator编辑器的压缩算法实现
1835 |      * 前5个hex字符保持不变,剩余27个字符压缩成18个字符
1836 |      */
1837 |     private uuidToCompressedId(uuid: string): string {
1838 |         const BASE64_KEYS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
1839 |         
1840 |         // 移除连字符并转为小写
1841 |         const cleanUuid = uuid.replace(/-/g, '').toLowerCase();
1842 |         
1843 |         // 确保UUID有效
1844 |         if (cleanUuid.length !== 32) {
1845 |             return uuid; // 如果不是有效的UUID,返回原始值
1846 |         }
1847 |         
1848 |         // Cocos Creator的压缩算法:前5个字符保持不变,剩余27个字符压缩成18个字符
1849 |         let result = cleanUuid.substring(0, 5);
1850 |         
1851 |         // 剩余27个字符需要压缩成18个字符
1852 |         const remainder = cleanUuid.substring(5);
1853 |         
1854 |         // 每3个hex字符压缩成2个字符
1855 |         for (let i = 0; i < remainder.length; i += 3) {
1856 |             const hex1 = remainder[i] || '0';
1857 |             const hex2 = remainder[i + 1] || '0';
1858 |             const hex3 = remainder[i + 2] || '0';
1859 |             
1860 |             // 将3个hex字符(12位)转换为2个base64字符
1861 |             const value = parseInt(hex1 + hex2 + hex3, 16);
1862 |             
1863 |             // 12位分成两个6位
1864 |             const high6 = (value >> 6) & 63;
1865 |             const low6 = value & 63;
1866 |             
1867 |             result += BASE64_KEYS[high6] + BASE64_KEYS[low6];
1868 |         }
1869 |         
1870 |         return result;
1871 |     }
1872 | 
1873 |     /**
1874 |      * 创建组件对象
1875 |      */
1876 |     private createComponentObject(componentData: any, nodeIndex: number, context?: { 
1877 |         nodeUuidToIndex?: Map<string, number>,
1878 |         componentUuidToIndex?: Map<string, number>
1879 |     }): any {
1880 |         let componentType = componentData.type || componentData.__type__ || 'cc.Component';
1881 |         const enabled = componentData.enabled !== undefined ? componentData.enabled : true;
1882 |         
1883 |         // console.log(`创建组件对象 - 原始类型: ${componentType}`);
1884 |         // console.log('组件完整数据:', JSON.stringify(componentData, null, 2));
1885 |         
1886 |         // 处理脚本组件 - MCP接口已经返回正确的压缩UUID格式
1887 |         if (componentType && !componentType.startsWith('cc.')) {
1888 |             console.log(`使用脚本组件压缩UUID类型: ${componentType}`);
1889 |         }
1890 |         
1891 |         // 基础组件结构
1892 |         const component: any = {
1893 |             "__type__": componentType,
1894 |             "_name": "",
1895 |             "_objFlags": 0,
1896 |             "__editorExtras__": {},
1897 |             "node": { "__id__": nodeIndex },
1898 |             "_enabled": enabled
1899 |         };
1900 |         
1901 |         // 提前设置 __prefab 属性占位符,后续会被正确设置
1902 |         component.__prefab = null;
1903 |         
1904 |         // 根据组件类型添加特定属性
1905 |         if (componentType === 'cc.UITransform') {
1906 |             const contentSize = componentData.properties?.contentSize?.value || { width: 100, height: 100 };
1907 |             const anchorPoint = componentData.properties?.anchorPoint?.value || { x: 0.5, y: 0.5 };
1908 |             
1909 |             component._contentSize = {
1910 |                 "__type__": "cc.Size",
1911 |                 "width": contentSize.width,
1912 |                 "height": contentSize.height
1913 |             };
1914 |             component._anchorPoint = {
1915 |                 "__type__": "cc.Vec2",
1916 |                 "x": anchorPoint.x,
1917 |                 "y": anchorPoint.y
1918 |             };
1919 |         } else if (componentType === 'cc.Sprite') {
1920 |             // 处理Sprite组件的spriteFrame引用
1921 |             const spriteFrameProp = componentData.properties?._spriteFrame || componentData.properties?.spriteFrame;
1922 |             if (spriteFrameProp) {
1923 |                 component._spriteFrame = this.processComponentProperty(spriteFrameProp, context);
1924 |             } else {
1925 |                 component._spriteFrame = null;
1926 |             }
1927 |             
1928 |             component._type = componentData.properties?._type?.value ?? 0;
1929 |             component._fillType = componentData.properties?._fillType?.value ?? 0;
1930 |             component._sizeMode = componentData.properties?._sizeMode?.value ?? 1;
1931 |             component._fillCenter = { "__type__": "cc.Vec2", "x": 0, "y": 0 };
1932 |             component._fillStart = componentData.properties?._fillStart?.value ?? 0;
1933 |             component._fillRange = componentData.properties?._fillRange?.value ?? 0;
1934 |             component._isTrimmedMode = componentData.properties?._isTrimmedMode?.value ?? true;
1935 |             component._useGrayscale = componentData.properties?._useGrayscale?.value ?? false;
1936 |             
1937 |             // 调试:打印Sprite组件的所有属性(已注释)
1938 |             // console.log('Sprite组件属性:', JSON.stringify(componentData.properties, null, 2));
1939 |             component._atlas = null;
1940 |             component._id = "";
1941 |         } else if (componentType === 'cc.Button') {
1942 |             component._interactable = true;
1943 |             component._transition = 3;
1944 |             component._normalColor = { "__type__": "cc.Color", "r": 255, "g": 255, "b": 255, "a": 255 };
1945 |             component._hoverColor = { "__type__": "cc.Color", "r": 211, "g": 211, "b": 211, "a": 255 };
1946 |             component._pressedColor = { "__type__": "cc.Color", "r": 255, "g": 255, "b": 255, "a": 255 };
1947 |             component._disabledColor = { "__type__": "cc.Color", "r": 124, "g": 124, "b": 124, "a": 255 };
1948 |             component._normalSprite = null;
1949 |             component._hoverSprite = null;
1950 |             component._pressedSprite = null;
1951 |             component._disabledSprite = null;
1952 |             component._duration = 0.1;
1953 |             component._zoomScale = 1.2;
1954 |             // 处理Button的target引用
1955 |             const targetProp = componentData.properties?._target || componentData.properties?.target;
1956 |             if (targetProp) {
1957 |                 component._target = this.processComponentProperty(targetProp, context);
1958 |             } else {
1959 |                 component._target = { "__id__": nodeIndex }; // 默认指向自身节点
1960 |             }
1961 |             component._clickEvents = [];
1962 |             component._id = "";
1963 |         } else if (componentType === 'cc.Label') {
1964 |             component._string = componentData.properties?._string?.value || "Label";
1965 |             component._horizontalAlign = 1;
1966 |             component._verticalAlign = 1;
1967 |             component._actualFontSize = 20;
1968 |             component._fontSize = 20;
1969 |             component._fontFamily = "Arial";
1970 |             component._lineHeight = 25;
1971 |             component._overflow = 0;
1972 |             component._enableWrapText = true;
1973 |             component._font = null;
1974 |             component._isSystemFontUsed = true;
1975 |             component._spacingX = 0;
1976 |             component._isItalic = false;
1977 |             component._isBold = false;
1978 |             component._isUnderline = false;
1979 |             component._underlineHeight = 2;
1980 |             component._cacheMode = 0;
1981 |             component._id = "";
1982 |         } else if (componentData.properties) {
1983 |             // 处理所有组件的属性(包括内置组件和自定义脚本组件)
1984 |             for (const [key, value] of Object.entries(componentData.properties)) {
1985 |                 if (key === 'node' || key === 'enabled' || key === '__type__' || 
1986 |                     key === 'uuid' || key === 'name' || key === '__scriptAsset' || key === '_objFlags') {
1987 |                     continue; // 跳过这些特殊属性,包括_objFlags
1988 |                 }
1989 |                 
1990 |                 // 对于以下划线开头的属性,需要特殊处理
1991 |                 if (key.startsWith('_')) {
1992 |                     // 确保属性名保持原样(包括下划线)
1993 |                     const propValue = this.processComponentProperty(value, context);
1994 |                     if (propValue !== undefined) {
1995 |                         component[key] = propValue;
1996 |                     }
1997 |                 } else {
1998 |                     // 非下划线开头的属性正常处理
1999 |                     const propValue = this.processComponentProperty(value, context);
2000 |                     if (propValue !== undefined) {
2001 |                         component[key] = propValue;
2002 |                     }
2003 |                 }
2004 |             }
2005 |         }
2006 |         
2007 |         // 确保 _id 在最后位置
2008 |         const _id = component._id || "";
2009 |         delete component._id;
2010 |         component._id = _id;
2011 |         
2012 |         return component;
2013 |     }
2014 | 
2015 |     /**
2016 |      * 处理组件属性值,确保格式与手动创建的预制体一致
2017 |      */
2018 |     private processComponentProperty(propData: any, context?: { 
2019 |         nodeUuidToIndex?: Map<string, number>,
2020 |         componentUuidToIndex?: Map<string, number>
2021 |     }): any {
2022 |         if (!propData || typeof propData !== 'object') {
2023 |             return propData;
2024 |         }
2025 | 
2026 |         const value = propData.value;
2027 |         const type = propData.type;
2028 | 
2029 |         // 处理null值
2030 |         if (value === null || value === undefined) {
2031 |             return null;
2032 |         }
2033 | 
2034 |         // 处理空UUID对象,转换为null
2035 |         if (value && typeof value === 'object' && value.uuid === '') {
2036 |             return null;
2037 |         }
2038 | 
2039 |         // 处理节点引用
2040 |         if (type === 'cc.Node' && value?.uuid) {
2041 |             // 在预制体中,节点引用需要转换为 __id__ 形式
2042 |             if (context?.nodeUuidToIndex && context.nodeUuidToIndex.has(value.uuid)) {
2043 |                 // 内部引用:转换为__id__格式
2044 |                 return {
2045 |                     "__id__": context.nodeUuidToIndex.get(value.uuid)
2046 |                 };
2047 |             }
2048 |             // 外部引用:设置为null,因为外部节点不属于预制体结构
2049 |             console.warn(`Node reference UUID ${value.uuid} not found in prefab context, setting to null (external reference)`);
2050 |             return null;
2051 |         }
2052 | 
2053 |         // 处理资源引用(预制体、纹理、精灵帧等)
2054 |         if (value?.uuid && (
2055 |             type === 'cc.Prefab' || 
2056 |             type === 'cc.Texture2D' || 
2057 |             type === 'cc.SpriteFrame' ||
2058 |             type === 'cc.Material' ||
2059 |             type === 'cc.AnimationClip' ||
2060 |             type === 'cc.AudioClip' ||
2061 |             type === 'cc.Font' ||
2062 |             type === 'cc.Asset'
2063 |         )) {
2064 |             // 对于预制体引用,保持原始UUID格式
2065 |             const uuidToUse = type === 'cc.Prefab' ? value.uuid : this.uuidToCompressedId(value.uuid);
2066 |             return {
2067 |                 "__uuid__": uuidToUse,
2068 |                 "__expectedType__": type
2069 |             };
2070 |         }
2071 | 
2072 |         // 处理组件引用(包括具体的组件类型如cc.Label, cc.Button等)
2073 |         if (value?.uuid && (type === 'cc.Component' || 
2074 |             type === 'cc.Label' || type === 'cc.Button' || type === 'cc.Sprite' || 
2075 |             type === 'cc.UITransform' || type === 'cc.RigidBody2D' || 
2076 |             type === 'cc.BoxCollider2D' || type === 'cc.Animation' || 
2077 |             type === 'cc.AudioSource' || (type?.startsWith('cc.') && !type.includes('@')))) {
2078 |             // 在预制体中,组件引用也需要转换为 __id__ 形式
2079 |             if (context?.componentUuidToIndex && context.componentUuidToIndex.has(value.uuid)) {
2080 |                 // 内部引用:转换为__id__格式
2081 |                 console.log(`Component reference ${type} UUID ${value.uuid} found in prefab context, converting to __id__`);
2082 |                 return {
2083 |                     "__id__": context.componentUuidToIndex.get(value.uuid)
2084 |                 };
2085 |             }
2086 |             // 外部引用:设置为null,因为外部组件不属于预制体结构
2087 |             console.warn(`Component reference ${type} UUID ${value.uuid} not found in prefab context, setting to null (external reference)`);
2088 |             return null;
2089 |         }
2090 | 
2091 |         // 处理复杂类型,添加__type__标记
2092 |         if (value && typeof value === 'object') {
2093 |             if (type === 'cc.Color') {
2094 |                 return {
2095 |                     "__type__": "cc.Color",
2096 |                     "r": Math.min(255, Math.max(0, Number(value.r) || 0)),
2097 |                     "g": Math.min(255, Math.max(0, Number(value.g) || 0)),
2098 |                     "b": Math.min(255, Math.max(0, Number(value.b) || 0)),
2099 |                     "a": value.a !== undefined ? Math.min(255, Math.max(0, Number(value.a))) : 255
2100 |                 };
2101 |             } else if (type === 'cc.Vec3') {
2102 |                 return {
2103 |                     "__type__": "cc.Vec3",
2104 |                     "x": Number(value.x) || 0,
2105 |                     "y": Number(value.y) || 0,
2106 |                     "z": Number(value.z) || 0
2107 |                 };
2108 |             } else if (type === 'cc.Vec2') {
2109 |                 return {
2110 |                     "__type__": "cc.Vec2", 
2111 |                     "x": Number(value.x) || 0,
2112 |                     "y": Number(value.y) || 0
2113 |                 };
2114 |             } else if (type === 'cc.Size') {
2115 |                 return {
2116 |                     "__type__": "cc.Size",
2117 |                     "width": Number(value.width) || 0,
2118 |                     "height": Number(value.height) || 0
2119 |                 };
2120 |             } else if (type === 'cc.Quat') {
2121 |                 return {
2122 |                     "__type__": "cc.Quat",
2123 |                     "x": Number(value.x) || 0,
2124 |                     "y": Number(value.y) || 0,
2125 |                     "z": Number(value.z) || 0,
2126 |                     "w": value.w !== undefined ? Number(value.w) : 1
2127 |                 };
2128 |             }
2129 |         }
2130 | 
2131 |         // 处理数组类型
2132 |         if (Array.isArray(value)) {
2133 |             // 节点数组
2134 |             if (propData.elementTypeData?.type === 'cc.Node') {
2135 |                 return value.map(item => {
2136 |                     if (item?.uuid && context?.nodeUuidToIndex?.has(item.uuid)) {
2137 |                         return { "__id__": context.nodeUuidToIndex.get(item.uuid) };
2138 |                     }
2139 |                     return null;
2140 |                 }).filter(item => item !== null);
2141 |             }
2142 |             
2143 |             // 资源数组
2144 |             if (propData.elementTypeData?.type && propData.elementTypeData.type.startsWith('cc.')) {
2145 |                 return value.map(item => {
2146 |                     if (item?.uuid) {
2147 |                         return {
2148 |                             "__uuid__": this.uuidToCompressedId(item.uuid),
2149 |                             "__expectedType__": propData.elementTypeData.type
2150 |                         };
2151 |                     }
2152 |                     return null;
2153 |                 }).filter(item => item !== null);
2154 |             }
2155 | 
2156 |             // 基础类型数组
2157 |             return value.map(item => item?.value !== undefined ? item.value : item);
2158 |         }
2159 | 
2160 |         // 其他复杂对象类型,保持原样但确保有__type__标记
2161 |         if (value && typeof value === 'object' && type && type.startsWith('cc.')) {
2162 |             return {
2163 |                 "__type__": type,
2164 |                 ...value
2165 |             };
2166 |         }
2167 | 
2168 |         return value;
2169 |     }
2170 | 
2171 |     /**
2172 |      * 创建符合引擎标准的节点对象
2173 |      */
2174 |     private createEngineStandardNode(nodeData: any, parentNodeIndex: number | null, nodeName?: string): any {
2175 |         // 调试:打印原始节点数据(已注释)
2176 |         // console.log('原始节点数据:', JSON.stringify(nodeData, null, 2));
2177 |         
2178 |         // 提取节点的基本属性
2179 |         const getValue = (prop: any) => {
2180 |             if (prop?.value !== undefined) return prop.value;
2181 |             if (prop !== undefined) return prop;
2182 |             return null;
2183 |         };
2184 |         
2185 |         const position = getValue(nodeData.position) || getValue(nodeData.value?.position) || { x: 0, y: 0, z: 0 };
2186 |         const rotation = getValue(nodeData.rotation) || getValue(nodeData.value?.rotation) || { x: 0, y: 0, z: 0, w: 1 };
2187 |         const scale = getValue(nodeData.scale) || getValue(nodeData.value?.scale) || { x: 1, y: 1, z: 1 };
2188 |         const active = getValue(nodeData.active) ?? getValue(nodeData.value?.active) ?? true;
2189 |         const name = nodeName || getValue(nodeData.name) || getValue(nodeData.value?.name) || 'Node';
2190 |         const layer = getValue(nodeData.layer) || getValue(nodeData.value?.layer) || 1073741824;
2191 | 
2192 |         // 调试输出
2193 |         console.log(`创建节点: ${name}, parentNodeIndex: ${parentNodeIndex}`);
2194 | 
2195 |         const parentRef = parentNodeIndex !== null ? { "__id__": parentNodeIndex } : null;
2196 |         console.log(`节点 ${name} 的父节点引用:`, parentRef);
2197 | 
2198 |         return {
2199 |             "__type__": "cc.Node",
2200 |             "_name": name,
2201 |             "_objFlags": 0,
2202 |             "__editorExtras__": {},
2203 |             "_parent": parentRef,
2204 |             "_children": [], // 子节点引用将在递归过程中动态添加
2205 |             "_active": active,
2206 |             "_components": [], // 组件引用将在处理组件时动态添加
2207 |             "_prefab": { "__id__": 0 }, // 临时值,后续会被正确设置
2208 |             "_lpos": {
2209 |                 "__type__": "cc.Vec3",
2210 |                 "x": position.x,
2211 |                 "y": position.y,
2212 |                 "z": position.z
2213 |             },
2214 |             "_lrot": {
2215 |                 "__type__": "cc.Quat",
2216 |                 "x": rotation.x,
2217 |                 "y": rotation.y,
2218 |                 "z": rotation.z,
2219 |                 "w": rotation.w
2220 |             },
2221 |             "_lscale": {
2222 |                 "__type__": "cc.Vec3",
2223 |                 "x": scale.x,
2224 |                 "y": scale.y,
2225 |                 "z": scale.z
2226 |             },
2227 |             "_mobility": 0,
2228 |             "_layer": layer,
2229 |             "_euler": {
2230 |                 "__type__": "cc.Vec3",
2231 |                 "x": 0,
2232 |                 "y": 0,
2233 |                 "z": 0
2234 |             },
2235 |             "_id": ""
2236 |         };
2237 |     }
2238 | 
2239 |     /**
2240 |      * 从节点数据中提取UUID
2241 |      */
2242 |     private extractNodeUuid(nodeData: any): string | null {
2243 |         if (!nodeData) return null;
2244 |         
2245 |         // 尝试多种方式获取UUID
2246 |         const sources = [
2247 |             nodeData.uuid,
2248 |             nodeData.value?.uuid,
2249 |             nodeData.__uuid__,
2250 |             nodeData.value?.__uuid__,
2251 |             nodeData.id,
2252 |             nodeData.value?.id
2253 |         ];
2254 |         
2255 |         for (const source of sources) {
2256 |             if (typeof source === 'string' && source.length > 0) {
2257 |                 return source;
2258 |             }
2259 |         }
2260 |         
2261 |         return null;
2262 |     }
2263 | 
2264 |     /**
2265 |      * 创建最小化的节点对象,不包含任何组件以避免依赖问题
2266 |      */
2267 |     private createMinimalNode(nodeData: any, nodeName?: string): any {
2268 |         // 提取节点的基本属性
2269 |         const getValue = (prop: any) => {
2270 |             if (prop?.value !== undefined) return prop.value;
2271 |             if (prop !== undefined) return prop;
2272 |             return null;
2273 |         };
2274 |         
2275 |         const position = getValue(nodeData.position) || getValue(nodeData.value?.position) || { x: 0, y: 0, z: 0 };
2276 |         const rotation = getValue(nodeData.rotation) || getValue(nodeData.value?.rotation) || { x: 0, y: 0, z: 0, w: 1 };
2277 |         const scale = getValue(nodeData.scale) || getValue(nodeData.value?.scale) || { x: 1, y: 1, z: 1 };
2278 |         const active = getValue(nodeData.active) ?? getValue(nodeData.value?.active) ?? true;
2279 |         const name = nodeName || getValue(nodeData.name) || getValue(nodeData.value?.name) || 'Node';
2280 |         const layer = getValue(nodeData.layer) || getValue(nodeData.value?.layer) || 33554432;
2281 | 
2282 |         return {
2283 |             "__type__": "cc.Node",
2284 |             "_name": name,
2285 |             "_objFlags": 0,
2286 |             "_parent": null,
2287 |             "_children": [],
2288 |             "_active": active,
2289 |             "_components": [], // 空的组件数组,避免组件依赖问题
2290 |             "_prefab": {
2291 |                 "__id__": 2
2292 |             },
2293 |             "_lpos": {
2294 |                 "__type__": "cc.Vec3",
2295 |                 "x": position.x,
2296 |                 "y": position.y,
2297 |                 "z": position.z
2298 |             },
2299 |             "_lrot": {
2300 |                 "__type__": "cc.Quat",
2301 |                 "x": rotation.x,
2302 |                 "y": rotation.y,
2303 |                 "z": rotation.z,
2304 |                 "w": rotation.w
2305 |             },
2306 |             "_lscale": {
2307 |                 "__type__": "cc.Vec3",
2308 |                 "x": scale.x,
2309 |                 "y": scale.y,
2310 |                 "z": scale.z
2311 |             },
2312 |             "_layer": layer,
2313 |             "_euler": {
2314 |                 "__type__": "cc.Vec3",
2315 |                 "x": 0,
2316 |                 "y": 0,
2317 |                 "z": 0
2318 |             },
2319 |             "_id": ""
2320 |         };
2321 |     }
2322 | 
2323 |     /**
2324 |      * 创建标准的 meta 文件内容
2325 |      */
2326 |     private createStandardMetaContent(prefabName: string, prefabUuid: string): any {
2327 |         return {
2328 |             "ver": "2.0.3",
2329 |             "importer": "prefab",
2330 |             "imported": true,
2331 |             "uuid": prefabUuid,
2332 |             "files": [
2333 |                 ".json"
2334 |             ],
2335 |             "subMetas": {},
2336 |             "userData": {
2337 |                 "syncNodeName": prefabName,
2338 |                 "hasIcon": false
2339 |             }
2340 |         };
2341 |     }
2342 | 
2343 |     /**
2344 |      * 尝试将原始节点转换为预制体实例
2345 |      */
2346 |     private async convertNodeToPrefabInstance(nodeUuid: string, prefabUuid: string, prefabPath: string): Promise<{ success: boolean; error?: string }> {
2347 |         return new Promise((resolve) => {
2348 |             // 这个功能需要深入的场景编辑器集成,暂时返回失败
2349 |             // 在实际的引擎中,这涉及到复杂的预制体实例化和节点替换逻辑
2350 |             console.log('节点转换为预制体实例的功能需要更深入的引擎集成');
2351 |             resolve({
2352 |                 success: false,
2353 |                 error: '节点转换为预制体实例需要更深入的引擎集成支持'
2354 |             });
2355 |         });
2356 |     }
2357 | 
2358 |     private async restorePrefabNode(nodeUuid: string, assetUuid: string): Promise<ToolResponse> {
2359 |         return new Promise((resolve) => {
2360 |             // 使用官方API restore-prefab 还原预制体节点
2361 |             (Editor.Message.request as any)('scene', 'restore-prefab', nodeUuid, assetUuid).then(() => {
2362 |                 resolve({
2363 |                     success: true,
2364 |                     data: {
2365 |                         nodeUuid: nodeUuid,
2366 |                         assetUuid: assetUuid,
2367 |                         message: '预制体节点还原成功'
2368 |                     }
2369 |                 });
2370 |             }).catch((error: any) => {
2371 |                 resolve({
2372 |                     success: false,
2373 |                     error: `预制体节点还原失败: ${error.message}`
2374 |                 });
2375 |             });
2376 |         });
2377 |     }
2378 | 
2379 |     // 基于官方预制体格式的新实现方法
2380 |     private async getNodeDataForPrefab(nodeUuid: string): Promise<{ success: boolean; data?: any; error?: string }> {
2381 |         return new Promise((resolve) => {
2382 |             Editor.Message.request('scene', 'query-node', nodeUuid).then((nodeData: any) => {
2383 |                 if (!nodeData) {
2384 |                     resolve({ success: false, error: '节点不存在' });
2385 |                     return;
2386 |                 }
2387 |                 resolve({ success: true, data: nodeData });
2388 |             }).catch((error: any) => {
2389 |                 resolve({ success: false, error: error.message });
2390 |             });
2391 |         });
2392 |     }
2393 | 
2394 |     private async createStandardPrefabData(nodeData: any, prefabName: string, prefabUuid: string): Promise<any[]> {
2395 |         // 基于官方Canvas.prefab格式创建预制体数据结构
2396 |         const prefabData: any[] = [];
2397 |         let currentId = 0;
2398 | 
2399 |         // 第一个元素:cc.Prefab 资源对象
2400 |         const prefabAsset = {
2401 |             "__type__": "cc.Prefab",
2402 |             "_name": prefabName,
2403 |             "_objFlags": 0,
2404 |             "__editorExtras__": {},
2405 |             "_native": "",
2406 |             "data": {
2407 |                 "__id__": 1
2408 |             },
2409 |             "optimizationPolicy": 0,
2410 |             "persistent": false
2411 |         };
2412 |         prefabData.push(prefabAsset);
2413 |         currentId++;
2414 | 
2415 |         // 第二个元素:根节点
2416 |         const rootNode = await this.createNodeObject(nodeData, null, prefabData, currentId);
2417 |         prefabData.push(rootNode.node);
2418 |         currentId = rootNode.nextId;
2419 | 
2420 |         // 添加根节点的 PrefabInfo - 修复asset引用使用UUID
2421 |         const rootPrefabInfo = {
2422 |             "__type__": "cc.PrefabInfo",
2423 |             "root": {
2424 |                 "__id__": 1
2425 |             },
2426 |             "asset": {
2427 |                 "__uuid__": prefabUuid
2428 |             },
2429 |             "fileId": this.generateFileId(),
2430 |             "instance": null,
2431 |             "targetOverrides": [],
2432 |             "nestedPrefabInstanceRoots": []
2433 |         };
2434 |         prefabData.push(rootPrefabInfo);
2435 | 
2436 |         return prefabData;
2437 |     }
2438 | 
2439 | 
2440 |     private async createNodeObject(nodeData: any, parentId: number | null, prefabData: any[], currentId: number): Promise<{ node: any; nextId: number }> {
2441 |         const nodeId = currentId++;
2442 |         
2443 |         // 提取节点的基本属性 - 适配query-node-tree的数据格式
2444 |         const getValue = (prop: any) => {
2445 |             if (prop?.value !== undefined) return prop.value;
2446 |             if (prop !== undefined) return prop;
2447 |             return null;
2448 |         };
2449 |         
2450 |         const position = getValue(nodeData.position) || getValue(nodeData.value?.position) || { x: 0, y: 0, z: 0 };
2451 |         const rotation = getValue(nodeData.rotation) || getValue(nodeData.value?.rotation) || { x: 0, y: 0, z: 0, w: 1 };
2452 |         const scale = getValue(nodeData.scale) || getValue(nodeData.value?.scale) || { x: 1, y: 1, z: 1 };
2453 |         const active = getValue(nodeData.active) ?? getValue(nodeData.value?.active) ?? true;
2454 |         const name = getValue(nodeData.name) || getValue(nodeData.value?.name) || 'Node';
2455 |         const layer = getValue(nodeData.layer) || getValue(nodeData.value?.layer) || 33554432;
2456 | 
2457 |         const node: any = {
2458 |             "__type__": "cc.Node",
2459 |             "_name": name,
2460 |             "_objFlags": 0,
2461 |             "__editorExtras__": {},
2462 |             "_parent": parentId !== null ? { "__id__": parentId } : null,
2463 |             "_children": [],
2464 |             "_active": active,
2465 |             "_components": [],
2466 |             "_prefab": parentId === null ? {
2467 |                 "__id__": currentId++
2468 |             } : null,
2469 |             "_lpos": {
2470 |                 "__type__": "cc.Vec3",
2471 |                 "x": position.x,
2472 |                 "y": position.y,
2473 |                 "z": position.z
2474 |             },
2475 |             "_lrot": {
2476 |                 "__type__": "cc.Quat",
2477 |                 "x": rotation.x,
2478 |                 "y": rotation.y,
2479 |                 "z": rotation.z,
2480 |                 "w": rotation.w
2481 |             },
2482 |             "_lscale": {
2483 |                 "__type__": "cc.Vec3",
2484 |                 "x": scale.x,
2485 |                 "y": scale.y,
2486 |                 "z": scale.z
2487 |             },
2488 |             "_mobility": 0,
2489 |             "_layer": layer,
2490 |             "_euler": {
2491 |                 "__type__": "cc.Vec3",
2492 |                 "x": 0,
2493 |                 "y": 0,
2494 |                 "z": 0
2495 |             },
2496 |             "_id": ""
2497 |         };
2498 | 
2499 |         // 暂时跳过UITransform组件以避免_getDependComponent错误
2500 |         // 后续通过Engine API动态添加
2501 |         console.log(`节点 ${name} 暂时跳过UITransform组件,避免引擎依赖错误`);
2502 |         
2503 |         // 处理其他组件(暂时跳过,专注于修复UITransform问题)
2504 |         const components = this.extractComponentsFromNode(nodeData);
2505 |         if (components.length > 0) {
2506 |             console.log(`节点 ${name} 包含 ${components.length} 个其他组件,暂时跳过以专注于UITransform修复`);
2507 |         }
2508 | 
2509 |         // 处理子节点 - 使用query-node-tree获取的完整结构
2510 |         const childrenToProcess = this.getChildrenToProcess(nodeData);
2511 |         if (childrenToProcess.length > 0) {
2512 |             console.log(`=== 处理子节点 ===`);
2513 |             console.log(`节点 ${name} 包含 ${childrenToProcess.length} 个子节点`);
2514 |             
2515 |             for (let i = 0; i < childrenToProcess.length; i++) {
2516 |                 const childData = childrenToProcess[i];
2517 |                 const childName = childData.name || childData.value?.name || '未知';
2518 |                 console.log(`处理第${i + 1}个子节点: ${childName}`);
2519 |                 
2520 |                 try {
2521 |                     const childId = currentId;
2522 |                     node._children.push({ "__id__": childId });
2523 |                     
2524 |                     // 递归创建子节点
2525 |                     const childResult = await this.createNodeObject(childData, nodeId, prefabData, currentId);
2526 |                     prefabData.push(childResult.node);
2527 |                     currentId = childResult.nextId;
2528 |                     
2529 |                     // 子节点不需要PrefabInfo,只有根节点需要
2530 |                     // 子节点的_prefab应该设置为null
2531 |                     childResult.node._prefab = null;
2532 |                     
2533 |                     console.log(`✅ 成功添加子节点: ${childName}`);
2534 |                 } catch (error) {
2535 |                     console.error(`处理子节点 ${childName} 时出错:`, error);
2536 |                 }
2537 |             }
2538 |         }
2539 | 
2540 |         return { node, nextId: currentId };
2541 |     }
2542 | 
2543 |     // 从节点数据中提取组件信息
2544 |     private extractComponentsFromNode(nodeData: any): any[] {
2545 |         const components: any[] = [];
2546 |         
2547 |         // 从不同位置尝试获取组件数据
2548 |         const componentSources = [
2549 |             nodeData.__comps__,
2550 |             nodeData.components,
2551 |             nodeData.value?.__comps__,
2552 |             nodeData.value?.components
2553 |         ];
2554 |         
2555 |         for (const source of componentSources) {
2556 |             if (Array.isArray(source)) {
2557 |                 components.push(...source.filter(comp => comp && (comp.__type__ || comp.type)));
2558 |                 break; // 找到有效的组件数组就退出
2559 |             }
2560 |         }
2561 |         
2562 |         return components;
2563 |     }
2564 |     
2565 |     // 创建标准的组件对象
2566 |     private createStandardComponentObject(componentData: any, nodeId: number, prefabInfoId: number): any {
2567 |         const componentType = componentData.__type__ || componentData.type;
2568 |         
2569 |         if (!componentType) {
2570 |             console.warn('组件缺少类型信息:', componentData);
2571 |             return null;
2572 |         }
2573 |         
2574 |         // 基础组件结构 - 基于官方预制体格式
2575 |         const component: any = {
2576 |             "__type__": componentType,
2577 |             "_name": "",
2578 |             "_objFlags": 0,
2579 |             "node": {
2580 |                 "__id__": nodeId
2581 |             },
2582 |             "_enabled": this.getComponentPropertyValue(componentData, 'enabled', true),
2583 |             "__prefab": {
2584 |                 "__id__": prefabInfoId
2585 |             }
2586 |         };
2587 |         
2588 |         // 根据组件类型添加特定属性
2589 |         this.addComponentSpecificProperties(component, componentData, componentType);
2590 |         
2591 |         // 添加_id属性
2592 |         component._id = "";
2593 |         
2594 |         return component;
2595 |     }
2596 |     
2597 |     // 添加组件特定的属性
2598 |     private addComponentSpecificProperties(component: any, componentData: any, componentType: string): void {
2599 |         switch (componentType) {
2600 |             case 'cc.UITransform':
2601 |                 this.addUITransformProperties(component, componentData);
2602 |                 break;
2603 |             case 'cc.Sprite':
2604 |                 this.addSpriteProperties(component, componentData);
2605 |                 break;
2606 |             case 'cc.Label':
2607 |                 this.addLabelProperties(component, componentData);
2608 |                 break;
2609 |             case 'cc.Button':
2610 |                 this.addButtonProperties(component, componentData);
2611 |                 break;
2612 |             default:
2613 |                 // 对于未知类型的组件,复制所有安全的属性
2614 |                 this.addGenericProperties(component, componentData);
2615 |                 break;
2616 |         }
2617 |     }
2618 |     
2619 |     // UITransform组件属性
2620 |     private addUITransformProperties(component: any, componentData: any): void {
2621 |         component._contentSize = this.createSizeObject(
2622 |             this.getComponentPropertyValue(componentData, 'contentSize', { width: 100, height: 100 })
2623 |         );
2624 |         component._anchorPoint = this.createVec2Object(
2625 |             this.getComponentPropertyValue(componentData, 'anchorPoint', { x: 0.5, y: 0.5 })
2626 |         );
2627 |     }
2628 |     
2629 |     // Sprite组件属性
2630 |     private addSpriteProperties(component: any, componentData: any): void {
2631 |         component._visFlags = 0;
2632 |         component._customMaterial = null;
2633 |         component._srcBlendFactor = 2;
2634 |         component._dstBlendFactor = 4;
2635 |         component._color = this.createColorObject(
2636 |             this.getComponentPropertyValue(componentData, 'color', { r: 255, g: 255, b: 255, a: 255 })
2637 |         );
2638 |         component._spriteFrame = this.getComponentPropertyValue(componentData, 'spriteFrame', null);
2639 |         component._type = this.getComponentPropertyValue(componentData, 'type', 0);
2640 |         component._fillType = 0;
2641 |         component._sizeMode = this.getComponentPropertyValue(componentData, 'sizeMode', 1);
2642 |         component._fillCenter = this.createVec2Object({ x: 0, y: 0 });
2643 |         component._fillStart = 0;
2644 |         component._fillRange = 0;
2645 |         component._isTrimmedMode = true;
2646 |         component._useGrayscale = false;
2647 |         component._atlas = null;
2648 |     }
2649 |     
2650 |     // Label组件属性
2651 |     private addLabelProperties(component: any, componentData: any): void {
2652 |         component._visFlags = 0;
2653 |         component._customMaterial = null;
2654 |         component._srcBlendFactor = 2;
2655 |         component._dstBlendFactor = 4;
2656 |         component._color = this.createColorObject(
2657 |             this.getComponentPropertyValue(componentData, 'color', { r: 0, g: 0, b: 0, a: 255 })
2658 |         );
2659 |         component._string = this.getComponentPropertyValue(componentData, 'string', 'Label');
2660 |         component._horizontalAlign = 1;
2661 |         component._verticalAlign = 1;
2662 |         component._actualFontSize = 20;
2663 |         component._fontSize = this.getComponentPropertyValue(componentData, 'fontSize', 20);
2664 |         component._fontFamily = 'Arial';
2665 |         component._lineHeight = 40;
2666 |         component._overflow = 1;
2667 |         component._enableWrapText = false;
2668 |         component._font = null;
2669 |         component._isSystemFontUsed = true;
2670 |         component._isItalic = false;
2671 |         component._isBold = false;
2672 |         component._isUnderline = false;
2673 |         component._underlineHeight = 2;
2674 |         component._cacheMode = 0;
2675 |     }
2676 |     
2677 |     // Button组件属性
2678 |     private addButtonProperties(component: any, componentData: any): void {
2679 |         component.clickEvents = [];
2680 |         component._interactable = true;
2681 |         component._transition = 2;
2682 |         component._normalColor = this.createColorObject({ r: 214, g: 214, b: 214, a: 255 });
2683 |         component._hoverColor = this.createColorObject({ r: 211, g: 211, b: 211, a: 255 });
2684 |         component._pressedColor = this.createColorObject({ r: 255, g: 255, b: 255, a: 255 });
2685 |         component._disabledColor = this.createColorObject({ r: 124, g: 124, b: 124, a: 255 });
2686 |         component._duration = 0.1;
2687 |         component._zoomScale = 1.2;
2688 |     }
2689 |     
2690 |     // 添加通用属性
2691 |     private addGenericProperties(component: any, componentData: any): void {
2692 |         // 只复制安全的、已知的属性
2693 |         const safeProperties = ['enabled', 'color', 'string', 'fontSize', 'spriteFrame', 'type', 'sizeMode'];
2694 |         
2695 |         for (const prop of safeProperties) {
2696 |             if (componentData.hasOwnProperty(prop)) {
2697 |                 const value = this.getComponentPropertyValue(componentData, prop);
2698 |                 if (value !== undefined) {
2699 |                     component[`_${prop}`] = value;
2700 |                 }
2701 |             }
2702 |         }
2703 |     }
2704 |     
2705 |     // 创建Vec2对象
2706 |     private createVec2Object(data: any): any {
2707 |         return {
2708 |             "__type__": "cc.Vec2",
2709 |             "x": data?.x || 0,
2710 |             "y": data?.y || 0
2711 |         };
2712 |     }
2713 |     
2714 |     // 创建Vec3对象
2715 |     private createVec3Object(data: any): any {
2716 |         return {
2717 |             "__type__": "cc.Vec3",
2718 |             "x": data?.x || 0,
2719 |             "y": data?.y || 0,
2720 |             "z": data?.z || 0
2721 |         };
2722 |     }
2723 |     
2724 |     // 创建Size对象
2725 |     private createSizeObject(data: any): any {
2726 |         return {
2727 |             "__type__": "cc.Size",
2728 |             "width": data?.width || 100,
2729 |             "height": data?.height || 100
2730 |         };
2731 |     }
2732 |     
2733 |     // 创建Color对象
2734 |     private createColorObject(data: any): any {
2735 |         return {
2736 |             "__type__": "cc.Color",
2737 |             "r": data?.r ?? 255,
2738 |             "g": data?.g ?? 255,
2739 |             "b": data?.b ?? 255,
2740 |             "a": data?.a ?? 255
2741 |         };
2742 |     }
2743 | 
2744 |     // 判断是否应该复制组件属性
2745 |     private shouldCopyComponentProperty(key: string, value: any): boolean {
2746 |         // 跳过内部属性和已处理的属性
2747 |         if (key.startsWith('__') || key === '_enabled' || key === 'node' || key === 'enabled') {
2748 |             return false;
2749 |         }
2750 |         
2751 |         // 跳过函数和undefined值
2752 |         if (typeof value === 'function' || value === undefined) {
2753 |             return false;
2754 |         }
2755 |         
2756 |         return true;
2757 |     }
2758 | 
2759 | 
2760 |     // 获取组件属性值 - 重命名以避免冲突
2761 |     private getComponentPropertyValue(componentData: any, propertyName: string, defaultValue?: any): any {
2762 |         // 尝试直接获取属性
2763 |         if (componentData[propertyName] !== undefined) {
2764 |             return this.extractValue(componentData[propertyName]);
2765 |         }
2766 |         
2767 |         // 尝试从value属性中获取
2768 |         if (componentData.value && componentData.value[propertyName] !== undefined) {
2769 |             return this.extractValue(componentData.value[propertyName]);
2770 |         }
2771 |         
2772 |         // 尝试带下划线前缀的属性名
2773 |         const prefixedName = `_${propertyName}`;
2774 |         if (componentData[prefixedName] !== undefined) {
2775 |             return this.extractValue(componentData[prefixedName]);
2776 |         }
2777 |         
2778 |         return defaultValue;
2779 |     }
2780 |     
2781 |     // 提取属性值
2782 |     private extractValue(data: any): any {
2783 |         if (data === null || data === undefined) {
2784 |             return data;
2785 |         }
2786 |         
2787 |         // 如果有value属性,优先使用value
2788 |         if (typeof data === 'object' && data.hasOwnProperty('value')) {
2789 |             return data.value;
2790 |         }
2791 |         
2792 |         // 如果是引用对象,保持原样
2793 |         if (typeof data === 'object' && (data.__id__ !== undefined || data.__uuid__ !== undefined)) {
2794 |             return data;
2795 |         }
2796 |         
2797 |         return data;
2798 |     }
2799 | 
2800 |     private createStandardMetaData(prefabName: string, prefabUuid: string): any {
2801 |         return {
2802 |             "ver": "1.1.50",
2803 |             "importer": "prefab",
2804 |             "imported": true,
2805 |             "uuid": prefabUuid,
2806 |             "files": [
2807 |                 ".json"
2808 |             ],
2809 |             "subMetas": {},
2810 |             "userData": {
2811 |                 "syncNodeName": prefabName
2812 |             }
2813 |         };
2814 |     }
2815 | 
2816 |     private async savePrefabWithMeta(prefabPath: string, prefabData: any[], metaData: any): Promise<{ success: boolean; error?: string }> {
2817 |         try {
2818 |             const prefabContent = JSON.stringify(prefabData, null, 2);
2819 |             const metaContent = JSON.stringify(metaData, null, 2);
2820 | 
2821 |             // 确保路径以.prefab结尾
2822 |             const finalPrefabPath = prefabPath.endsWith('.prefab') ? prefabPath : `${prefabPath}.prefab`;
2823 |             const metaPath = `${finalPrefabPath}.meta`;
2824 | 
2825 |             // 使用asset-db API创建预制体文件
2826 |             await new Promise((resolve, reject) => {
2827 |                 Editor.Message.request('asset-db', 'create-asset', finalPrefabPath, prefabContent).then(() => {
2828 |                     resolve(true);
2829 |                 }).catch((error: any) => {
2830 |                     reject(error);
2831 |                 });
2832 |             });
2833 | 
2834 |             // 创建meta文件
2835 |             await new Promise((resolve, reject) => {
2836 |                 Editor.Message.request('asset-db', 'create-asset', metaPath, metaContent).then(() => {
2837 |                     resolve(true);
2838 |                 }).catch((error: any) => {
2839 |                     reject(error);
2840 |                 });
2841 |             });
2842 | 
2843 |             console.log(`=== 预制体保存完成 ===`);
2844 |             console.log(`预制体文件已保存: ${finalPrefabPath}`);
2845 |             console.log(`Meta文件已保存: ${metaPath}`);
2846 |             console.log(`预制体数组总长度: ${prefabData.length}`);
2847 |             console.log(`预制体根节点索引: ${prefabData.length - 1}`);
2848 | 
2849 |             return { success: true };
2850 |         } catch (error: any) {
2851 |             console.error('保存预制体文件时出错:', error);
2852 |             return { success: false, error: error.message };
2853 |         }
2854 |     }
2855 | 
2856 | }
```
Page 5/5FirstPrevNextLast