cocos2d-cli 1.2.1 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  /**
2
- * Fire/Prefab 文件工具模块
3
- * 提供直接读取和操作 .fire 场景文件和 .prefab 预制体文件的功能
2
+ * Fire/Prefab 文件工具模块
3
+ * 提供场景/预制体文件的读写和编辑器交互功能
4
4
  */
5
5
 
6
6
  const fs = require('fs');
@@ -33,18 +33,17 @@ function saveScene(scenePath, data) {
33
33
  }
34
34
 
35
35
  /**
36
- * 构建 ID 和索引映射(自动适配场景和预制体)
36
+ * 构建 ID 和索引映射
37
37
  */
38
38
  function buildMaps(data) {
39
- const idMap = {}; // _id -> index
40
- const indexMap = {}; // index -> { _id, name, path, type }
39
+ const idMap = {};
40
+ const indexMap = {};
41
41
  const prefab = isPrefab(data);
42
42
 
43
43
  function traverse(nodeIndex, parentPath = '') {
44
44
  const node = data[nodeIndex];
45
45
  if (!node) return;
46
46
 
47
- // 跳过非节点类型
48
47
  if (!node.__type__?.startsWith('cc.Node') && node.__type__ !== 'cc.Scene') {
49
48
  return;
50
49
  }
@@ -64,7 +63,6 @@ function buildMaps(data) {
64
63
  type: node.__type__
65
64
  };
66
65
 
67
- // 递归处理子节点
68
66
  if (node._children) {
69
67
  node._children.forEach(childRef => {
70
68
  traverse(childRef.__id__, nodePath);
@@ -72,13 +70,7 @@ function buildMaps(data) {
72
70
  }
73
71
  }
74
72
 
75
- if (prefab) {
76
- // 预制体:从索引 1(根节点)开始遍历
77
- traverse(1);
78
- } else {
79
- // 场景:从索引 1(Scene)开始遍历
80
- traverse(1);
81
- }
73
+ traverse(1);
82
74
 
83
75
  return { idMap, indexMap, prefab };
84
76
  }
@@ -87,12 +79,10 @@ function buildMaps(data) {
87
79
  * 查找节点索引
88
80
  */
89
81
  function findNodeIndex(data, indexMap, nodeRef) {
90
- // 如果是数字,直接返回
91
82
  if (/^\d+$/.test(nodeRef)) {
92
83
  return parseInt(nodeRef);
93
84
  }
94
85
 
95
- // 按名称/路径查找
96
86
  for (const [idx, info] of Object.entries(indexMap)) {
97
87
  if (info.name === nodeRef || info.path === nodeRef || info.path.endsWith('/' + nodeRef)) {
98
88
  return parseInt(idx);
@@ -103,35 +93,7 @@ function findNodeIndex(data, indexMap, nodeRef) {
103
93
  }
104
94
 
105
95
  /**
106
- * 递归收集节点及其所有子节点和组件的索引
107
- */
108
- function collectNodeAndChildren(data, nodeIndex, collected = new Set()) {
109
- if (collected.has(nodeIndex)) return collected;
110
-
111
- const node = data[nodeIndex];
112
- if (!node) return collected;
113
-
114
- collected.add(nodeIndex);
115
-
116
- // 收集所有组件
117
- if (node._components) {
118
- for (const compRef of node._components) {
119
- collected.add(compRef.__id__);
120
- }
121
- }
122
-
123
- // 递归收集子节点
124
- if (node._children) {
125
- for (const childRef of node._children) {
126
- collectNodeAndChildren(data, childRef.__id__, collected);
127
- }
128
- }
129
-
130
- return collected;
131
- }
132
-
133
- /**
134
- * 重建所有 __id__ 引用(删除元素后索引变化)
96
+ * 重建所有 __id__ 引用
135
97
  */
136
98
  function rebuildReferences(data, deletedIndices) {
137
99
  const indexMap = {};
@@ -165,175 +127,76 @@ function rebuildReferences(data, deletedIndices) {
165
127
  return indexMap;
166
128
  }
167
129
 
168
- /**
169
- * 重新排列数组,使其与 _children 顺序一致(节点后跟组件)
170
- */
171
- function reorderArrayToMatchChildren(data) {
172
- const newArray = [];
173
- const indexMap = {};
174
-
175
- newArray[0] = data[0];
176
- newArray[1] = data[1];
177
- indexMap[0] = 0;
178
- indexMap[1] = 1;
179
-
180
- const dataByIndex = {};
181
- for (let i = 0; i < data.length; i++) {
182
- if (data[i]) dataByIndex[i] = data[i];
183
- }
184
-
185
- function addNodeAndChildren(nodeIndex) {
186
- if (nodeIndex === null || nodeIndex === undefined) return;
187
-
188
- const node = data[nodeIndex];
189
- if (!node) return;
190
-
191
- const newIndex = newArray.length;
192
- indexMap[nodeIndex] = newIndex;
193
- newArray.push(node);
194
-
195
- if (node._components) {
196
- for (const compRef of node._components) {
197
- const compIndex = compRef.__id__;
198
- if (compIndex !== undefined && dataByIndex[compIndex]) {
199
- const compNewIndex = newArray.length;
200
- indexMap[compIndex] = compNewIndex;
201
- newArray.push(dataByIndex[compIndex]);
202
- }
203
- }
204
- }
205
-
206
- if (node._children) {
207
- for (const childRef of node._children) {
208
- addNodeAndChildren(childRef.__id__);
209
- }
210
- }
211
- }
212
-
213
- const scene = data[1];
214
- if (scene && scene._children) {
215
- for (const childRef of scene._children) {
216
- addNodeAndChildren(childRef.__id__);
217
- }
218
- }
219
-
220
- function addRootComponents(nodeIndex) {
221
- const node = data[nodeIndex];
222
- if (!node || !node._components) return;
223
-
224
- for (const compRef of node._components) {
225
- const compIndex = compRef.__id__;
226
- if (compIndex !== undefined && dataByIndex[compIndex] && indexMap[compIndex] === undefined) {
227
- const compNewIndex = newArray.length;
228
- indexMap[compIndex] = compNewIndex;
229
- newArray.push(dataByIndex[compIndex]);
230
- }
231
- }
232
- }
233
-
234
- addRootComponents(1);
235
- addRootComponents(2);
236
-
237
- function updateRefs(obj) {
238
- if (!obj || typeof obj !== 'object') return;
239
-
240
- if (obj.__id__ !== undefined) {
241
- const oldId = obj.__id__;
242
- if (indexMap[oldId] !== undefined) {
243
- obj.__id__ = indexMap[oldId];
244
- }
245
- } else {
246
- for (const key of Object.keys(obj)) {
247
- updateRefs(obj[key]);
248
- }
249
- }
250
- }
251
-
252
- for (const item of newArray) {
253
- updateRefs(item);
254
- }
255
-
256
- return newArray;
257
- }
258
-
259
130
  /**
260
131
  * 检查 CLI Helper 插件状态
261
- * @returns {object|null} - 插件状态信息,未启动返回 null
262
132
  */
263
133
  function checkPluginStatus() {
264
- const { execSync } = require('child_process');
134
+ const http = require('http');
265
135
 
266
- try {
267
- const result = execSync('curl.exe -s http://localhost:7455/status', {
268
- timeout: 3000,
269
- windowsHide: true,
270
- encoding: 'utf8'
136
+ return new Promise((resolve) => {
137
+ const req = http.request({
138
+ hostname: 'localhost',
139
+ port: 7455,
140
+ path: '/status',
141
+ method: 'GET'
142
+ }, (res) => {
143
+ let data = '';
144
+ res.on('data', chunk => data += chunk);
145
+ res.on('end', () => {
146
+ try {
147
+ resolve(JSON.parse(data));
148
+ } catch (e) {
149
+ resolve(null);
150
+ }
151
+ });
271
152
  });
272
- return JSON.parse(result);
273
- } catch (e) {
274
- return null;
275
- }
153
+
154
+ req.on('error', () => resolve(null));
155
+ req.setTimeout(3000, () => { req.destroy(); resolve(null); });
156
+ req.end();
157
+ });
276
158
  }
277
159
 
278
160
  /**
279
- * 触发 Cocos Creator 编辑器刷新资源
280
- * 智能判断:如果修改的场景就是当前打开的场景,才重新打开;否则只刷新资源
281
- * 编辑器有可能没打开,调用失败不报错
282
- * @param {string} scenePath - 场景文件路径(必须)
161
+ * 触发编辑器刷新
283
162
  */
284
163
  function refreshEditor(scenePath) {
285
- // 参数校验
286
- if (!scenePath) {
287
- console.log(JSON.stringify({ warning: 'refreshEditor: 未提供 scenePath 参数,编辑器不会自动刷新场景' }));
288
- return;
289
- }
164
+ if (!scenePath) return;
290
165
 
291
- const path = require('path');
292
166
  const http = require('http');
293
167
 
294
- // 转换场景路径为 db:// 格式
295
168
  const assetsPath = path.dirname(scenePath);
296
169
  const projectPath = path.dirname(assetsPath);
297
170
  const relativePath = path.relative(projectPath, scenePath).replace(/\\/g, '/');
298
171
  const targetSceneUrl = 'db://' + relativePath.replace(/^assets\//, 'assets/');
299
172
 
300
- // 先查询当前打开的场景
301
173
  const getCurrentScene = () => {
302
174
  return new Promise((resolve) => {
303
- const options = {
175
+ const req = http.request({
304
176
  hostname: 'localhost',
305
177
  port: 7455,
306
178
  path: '/current-scene',
307
179
  method: 'GET'
308
- };
309
-
310
- const req = http.request(options, (res) => {
180
+ }, (res) => {
311
181
  let data = '';
312
182
  res.on('data', chunk => data += chunk);
313
183
  res.on('end', () => {
314
184
  try {
315
- const result = JSON.parse(data);
316
- resolve(result.sceneUrl || null);
185
+ resolve(JSON.parse(data).sceneUrl || null);
317
186
  } catch (e) {
318
187
  resolve(null);
319
188
  }
320
189
  });
321
190
  });
322
-
323
191
  req.on('error', () => resolve(null));
324
- req.setTimeout(3000, () => {
325
- req.destroy();
326
- resolve(null);
327
- });
192
+ req.setTimeout(3000, () => { req.destroy(); resolve(null); });
328
193
  req.end();
329
194
  });
330
195
  };
331
196
 
332
- // 发送刷新请求
333
197
  const sendRefreshRequest = (sceneUrl) => {
334
198
  const postData = sceneUrl ? JSON.stringify({ sceneUrl }) : '';
335
-
336
- const options = {
199
+ const req = http.request({
337
200
  hostname: 'localhost',
338
201
  port: 7455,
339
202
  path: '/refresh',
@@ -342,80 +205,46 @@ function refreshEditor(scenePath) {
342
205
  'Content-Type': 'application/json',
343
206
  'Content-Length': Buffer.byteLength(postData)
344
207
  }
345
- };
346
-
347
- const req = http.request(options, (res) => {
348
- // 忽略响应
349
- });
350
-
351
- req.on('error', () => {
352
- // 插件未启动,静默处理
353
- });
354
-
355
- if (postData) {
356
- req.write(postData);
357
- }
208
+ }, () => {});
209
+ req.on('error', () => {});
210
+ if (postData) req.write(postData);
358
211
  req.end();
359
212
  };
360
213
 
361
- // 执行刷新逻辑
362
214
  getCurrentScene().then(currentSceneUrl => {
363
- if (currentSceneUrl && currentSceneUrl === targetSceneUrl) {
364
- // 是当前打开的场景,重新打开
365
- sendRefreshRequest(targetSceneUrl);
366
- } else {
367
- // 不是当前场景,只刷新资源
368
- sendRefreshRequest(null);
369
- }
215
+ sendRefreshRequest(currentSceneUrl === targetSceneUrl ? targetSceneUrl : null);
370
216
  });
371
217
  }
372
218
 
373
219
  /**
374
- * 检测并安装 CLI Helper 插件到项目
375
- * @param {string} scenePath - 场景文件路径
376
- * @returns {boolean} - 是否已安装
220
+ * 安装 CLI Helper 插件
377
221
  */
378
222
  function installPlugin(scenePath) {
379
223
  try {
380
- const fs = require('fs');
381
- const path = require('path');
382
-
383
- // 获取项目路径
384
224
  const assetsPath = path.dirname(scenePath);
385
225
  const projectPath = path.dirname(assetsPath);
386
226
  const packagesPath = path.join(projectPath, 'packages');
387
- const pluginPath = path.join(packagesPath, 'cocos-cli-helper');
227
+ const pluginPath = path.join(packagesPath, 'cocos2d-cli-helper');
388
228
 
389
- // 如果插件已存在,直接返回
390
- if (fs.existsSync(pluginPath)) {
391
- return true;
392
- }
229
+ if (fs.existsSync(pluginPath)) return true;
393
230
 
394
- // 创建 packages 目录
395
231
  if (!fs.existsSync(packagesPath)) {
396
232
  fs.mkdirSync(packagesPath, { recursive: true });
397
233
  }
398
234
 
399
- // 获取 CLI 自带的插件路径
400
- const cliPluginPath = path.join(__dirname, '..', '..', 'editor-plugin', 'cocos-cli-helper');
235
+ const cliPluginPath = path.join(__dirname, '..', '..', 'editor-plugin', 'cocos2d-cli-helper');
401
236
 
402
- if (!fs.existsSync(cliPluginPath)) {
403
- console.log('[CLI] 插件源文件不存在');
404
- return false;
405
- }
237
+ if (!fs.existsSync(cliPluginPath)) return false;
406
238
 
407
- // 复制插件
408
239
  fs.cpSync(cliPluginPath, pluginPath, { recursive: true });
409
- console.log('[CLI] CLI Helper 插件已安装到项目,请在编辑器中启用');
410
240
  return true;
411
241
  } catch (e) {
412
- console.log(`[CLI] 安装插件失败: ${e.message}`);
413
242
  return false;
414
243
  }
415
244
  }
416
245
 
417
246
  /**
418
- * 加载脚本映射(用于显示自定义脚本组件名称)
247
+ * 加载脚本映射
419
248
  */
420
249
  function loadScriptMap(scenePath) {
421
250
  const projectPath = path.dirname(path.dirname(scenePath));
@@ -428,62 +257,6 @@ function loadScriptMap(scenePath) {
428
257
  return {};
429
258
  }
430
259
 
431
- /**
432
- * 构建节点树输出(自动适配场景和预制体)
433
- */
434
- function buildTree(data, scriptMap, nodeIndex, prefix = '', isLast = true, isRoot = true) {
435
- const node = data[nodeIndex];
436
- if (!node) return '';
437
-
438
- // 跳过 Scene 类型(场景根节点)
439
- const isSceneRoot = node.__type__ === 'cc.Scene';
440
- const nodeName = isRoot ? 'Root' : (node._name || '(unnamed)');
441
- const active = node._active !== false ? '●' : '○';
442
- const uuidRegex = /^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/i;
443
-
444
- let result = '';
445
-
446
- // 场景根节点特殊处理
447
- if (isSceneRoot) {
448
- result = prefix + '[Scene]\n';
449
- } else {
450
- result = prefix + (isRoot ? '' : active + ' ') + nodeName + ' #' + nodeIndex;
451
-
452
- // 添加组件信息
453
- if (node._components && node._components.length > 0) {
454
- const comps = node._components.map(c => {
455
- const comp = data[c.__id__];
456
- if (!comp) return `? #${c.__id__}`;
457
- const typeName = comp.__type__;
458
- let displayName;
459
- if (uuidRegex.test(typeName)) {
460
- const scriptInfo = scriptMap[typeName];
461
- displayName = (scriptInfo && scriptInfo.name) ? scriptInfo.name : '[MissingScript]';
462
- } else if (typeName === 'MissingScript') {
463
- displayName = '[MissingScript]';
464
- } else {
465
- displayName = typeName.replace('cc.', '');
466
- }
467
- return `${displayName} #${c.__id__}`;
468
- }).join(', ');
469
- result += ` (${comps})`;
470
- }
471
-
472
- result += '\n';
473
- }
474
-
475
- // 处理子节点
476
- if (node._children && node._children.length > 0) {
477
- node._children.forEach((childRef, idx) => {
478
- const childIsLast = idx === node._children.length - 1;
479
- const childPrefix = prefix + (isSceneRoot ? '' : (isRoot ? '' : (isLast ? ' ' : '│ ')));
480
- result += buildTree(data, scriptMap, childRef.__id__, childPrefix, childIsLast, isSceneRoot);
481
- });
482
- }
483
-
484
- return result;
485
- }
486
-
487
260
  /**
488
261
  * 生成 fileId(用于 PrefabInfo)
489
262
  */
@@ -496,139 +269,32 @@ function generateFileId() {
496
269
  return result;
497
270
  }
498
271
 
499
- /**
500
- * 创建新预制体
501
- * @param {string} name - 预制体名称
502
- * @returns {Array} - 预制体数据
503
- *
504
- * 预制体结构说明:
505
- * [0] cc.Prefab - 预制体元数据
506
- * [1] cc.Node - 根节点,_prefab 指向最后一个 PrefabInfo
507
- * [2] cc.PrefabInfo - 根节点的 PrefabInfo(在最后)
508
- *
509
- * 当添加子节点时,结构变为:
510
- * [0] cc.Prefab
511
- * [1] cc.Node (根) _prefab -> [N]
512
- * [2] cc.Node (子1) _prefab -> [3]
513
- * [3] cc.PrefabInfo (子1的)
514
- * ...
515
- * [N] cc.PrefabInfo (根节点的,在最后)
516
- */
517
- function createPrefab(name) {
518
- const fileId = generateFileId();
519
- return [
520
- {
521
- "__type__": "cc.Prefab",
522
- "_name": "",
523
- "_objFlags": 0,
524
- "_native": "",
525
- "data": { "__id__": 1 },
526
- "optimizationPolicy": 0,
527
- "asyncLoadAssets": false,
528
- "readonly": false
529
- },
530
- {
531
- "__type__": "cc.Node",
532
- "_name": name,
533
- "_objFlags": 0,
534
- "_parent": null,
535
- "_children": [],
536
- "_active": true,
537
- "_components": [],
538
- "_prefab": { "__id__": 2 }, // 指向最后的 PrefabInfo
539
- "_opacity": 255,
540
- "_color": { "__type__": "cc.Color", "r": 255, "g": 255, "b": 255, "a": 255 },
541
- "_contentSize": { "__type__": "cc.Size", "width": 0, "height": 0 },
542
- "_anchorPoint": { "__type__": "cc.Vec2", "x": 0.5, "y": 0.5 },
543
- "_trs": { "__type__": "TypedArray", "ctor": "Float64Array", "array": [0, 0, 0, 0, 0, 0, 1, 1, 1, 1] },
544
- "_eulerAngles": { "__type__": "cc.Vec3", "x": 0, "y": 0, "z": 0 },
545
- "_skewX": 0,
546
- "_skewY": 0,
547
- "_is3DNode": false,
548
- "_groupIndex": 0,
549
- "groupIndex": 0,
550
- "_id": ""
551
- },
552
- {
553
- "__type__": "cc.PrefabInfo",
554
- "root": { "__id__": 1 },
555
- "asset": { "__id__": 0 },
556
- "fileId": fileId,
557
- "sync": false
558
- }
559
- ];
560
- }
561
-
562
- /**
563
- * 创建预制体节点数据(带 PrefabInfo)
564
- * @param {string} name - 节点名称
565
- * @param {number} parentId - 父节点索引
566
- * @param {number} rootId - 预制体根节点索引
567
- * @param {object} options - 可选参数
568
- */
569
- function createPrefabNodeData(name, parentId, rootId, options = {}) {
570
- const fileId = generateFileId();
571
- const nodeIndex = -1; // 占位,插入时确定
572
- const prefabInfoIndex = nodeIndex + 1; // PrefabInfo 紧跟节点
573
-
574
- const nodeData = {
575
- "__type__": "cc.Node",
576
- "_name": name,
577
- "_objFlags": 0,
578
- "_parent": { "__id__": parentId },
579
- "_children": [],
580
- "_active": options.active !== false,
581
- "_components": [],
582
- "_prefab": { "__id__": prefabInfoIndex },
583
- "_opacity": 255,
584
- "_color": { "__type__": "cc.Color", "r": 255, "g": 255, "b": 255, "a": 255 },
585
- "_contentSize": { "__type__": "cc.Size", "width": options.width || 0, "height": options.height || 0 },
586
- "_anchorPoint": { "__type__": "cc.Vec2", "x": 0.5, "y": 0.5 },
587
- "_trs": { "__type__": "TypedArray", "ctor": "Float64Array", "array": [options.x || 0, options.y || 0, 0, 0, 0, 0, 1, 1, 1, 1] },
588
- "_eulerAngles": { "__type__": "cc.Vec3", "x": 0, "y": 0, "z": 0 },
589
- "_skewX": 0,
590
- "_skewY": 0,
591
- "_is3DNode": false,
592
- "_groupIndex": 0,
593
- "groupIndex": 0,
594
- "_id": ""
595
- };
596
-
597
- const prefabInfo = {
598
- "__type__": "cc.PrefabInfo",
599
- "root": { "__id__": rootId },
600
- "asset": { "__id__": 0 },
601
- "fileId": fileId,
602
- "sync": false
603
- };
604
-
605
- return { nodeData, prefabInfo };
606
- }
607
-
608
272
  /**
609
273
  * 获取预制体根节点索引
610
274
  */
611
275
  function getPrefabRootIndex(data) {
612
276
  if (!isPrefab(data)) return null;
613
- return 1; // 预制体根节点固定在索引 1
277
+ return 1;
614
278
  }
615
279
 
616
280
  module.exports = {
281
+ // 文件操作
617
282
  loadScene,
618
283
  saveScene,
284
+ isPrefab,
285
+
286
+ // 索引映射
619
287
  buildMaps,
620
288
  findNodeIndex,
621
- collectNodeAndChildren,
622
289
  rebuildReferences,
623
- reorderArrayToMatchChildren,
290
+
291
+ // 编辑器交互
624
292
  refreshEditor,
625
293
  installPlugin,
626
294
  checkPluginStatus,
295
+
296
+ // 工具函数
627
297
  loadScriptMap,
628
- buildTree,
629
- isPrefab,
630
- createPrefab,
631
- createPrefabNodeData,
632
- getPrefabRootIndex,
633
- generateFileId
634
- };
298
+ generateFileId,
299
+ getPrefabRootIndex
300
+ };