[cli] 新增: add-spine-socket op(绑定 Spine Socket 到节点)

This commit is contained in:
furao
2026-06-12 10:42:39 +08:00
parent edd56bd03d
commit 7f4e447ba0
7 changed files with 74 additions and 0 deletions
+1
View File
@@ -70,6 +70,7 @@ node extensions/cc-3-8-x-mcp/cli/bin/cocos-mcp-cli.js <command>
|---|---|
| 加节点 | `add-node` |
| 加嵌套 prefab 实例(stub | `add-nested-prefab`parent + prefabUuid + name? + lpos? |
| 给 Spine 增加/更新 socket 绑定 | `add-spine-socket`node + path + target;同 path 幂等更新 target |
| 替换嵌套 prefab 的 asset uuid(保留 stub 结构) | `replace-nested-prefab`target + prefabUuid + clearOverrides? |
| 删节点 | `remove-node` |
| 清悬空嵌套实例根(删了一半的 prefab 残留:父引用没了但根 PrefabInfo 登记还在,残留 asset 仍被加载 → 404)| `sync-nested-roots`(无参,重建根 nestedPrefabInstanceRoots |
+1
View File
@@ -51,6 +51,7 @@ Supported ops:
reorder-children # 调子节点顺序(影响渲染层级)
bulk-set # 按 selectorbyComponent/byNamePrefix/byNameRegex)一次改一批
add-node / remove-node / clone-node
add-spine-socket # 给 sp.Skeleton 增加/更新 socket 绑定
add-component / set-component-ref # componentType 支持 @ccclass 名或压缩 classId
# set-component-ref 的 refSubNode 可用字符串数组走多层嵌套 stub
set-nested-component-field # 仅 stub 节点(嵌套 prefab)改组件字段
+2
View File
@@ -45,6 +45,7 @@ const { execSetSprite } = require('./ops/set-sprite.js');
const { execSetNodeColor } = require('./ops/set-node-color.js');
const { execReplaceNestedPrefab } = require('./ops/replace-nested-prefab.js');
const { execAddNestedPrefab } = require('./ops/add-nested-prefab.js');
const { execAddSpineSocket } = require('./ops/add-spine-socket.js');
const { execResetOverrides } = require('./ops/reset-overrides.js');
const { execEnsureMeta } = require('./ops/ensure-meta.js');
const { execSyncNestedRoots } = require('./ops/sync-nested-roots.js');
@@ -81,6 +82,7 @@ const OP_HANDLERS = {
'set-node-color': execSetNodeColor,
'replace-nested-prefab': execReplaceNestedPrefab,
'add-nested-prefab': execAddNestedPrefab,
'add-spine-socket': execAddSpineSocket,
'reset-overrides': execResetOverrides,
'ensure-meta': execEnsureMeta,
'sync-nested-roots': execSyncNestedRoots,
+2
View File
@@ -88,6 +88,7 @@ const T = {
constraintNum: 'number',
placeholder: 'string',
string: 'string',
path: 'string',
labelNode: 'string',
spriteNode: 'string',
subNode: 'any', // string | string[]
@@ -129,6 +130,7 @@ const SCHEMAS = {
'set-node-color': { required: ['node'], optional: ['r', 'g', 'b', 'a'] },
'replace-nested-prefab': { required: ['target', 'prefabUuid'], optional: ['clearOverrides'] },
'add-nested-prefab': { required: ['parent', 'prefabUuid'], optional: ['name', 'lpos'] },
'add-spine-socket': { required: ['node', 'path', 'target'], optional: [] },
'reset-overrides': { required: ['node'], optional: ['property', 'componentType', 'subNode', 'all'] },
// ensure-meta: 给 .ts/.json 文件创建 .metav4 uuid),让后续 className → classId 查表能命中
'ensure-meta': { required: ['path'], optional: [], typeOverrides: { path: 'string' } },
+50
View File
@@ -0,0 +1,50 @@
// add-spine-socket: 给普通节点上的 sp.Skeleton 增加 / 更新 Spine socket 绑定
// op: { op:'add-spine-socket', node, path, target }
//
// - node: 挂 sp.Skeleton 的节点
// - path: Spine socket path,例如 "root/zk/tou2"
// - target: 要绑定到该 socket 的 cc.Node
// - 幂等:同 path 已存在时只更新 target,不新增重复 socket 对象
'use strict';
const { makeSpineSocket } = require('../../primitives.js');
const { isStub, resolveNode, findComponent } = require('../helpers.js');
function execAddSpineSocket(prefabData, op) {
const { elements } = prefabData;
const { node: nodeSelector, path, target: targetSelector } = op;
if (typeof path !== 'string' || path.length === 0) {
throw new Error(`editPrefab [add-spine-socket]: path 必须是非空字符串`);
}
const { node, nodeId } = resolveNode(prefabData, nodeSelector, 'add-spine-socket');
if (isStub(elements, node)) {
throw new Error(`editPrefab [add-spine-socket]: 节点 "${node._name}" 是 stub 代理,不支持直接改子 prefab 的 sp.Skeleton`);
}
const skeleton = findComponent(elements, node, 'sp.Skeleton');
if (!skeleton) {
throw new Error(`editPrefab [add-spine-socket]: 节点 "${node._name}" 上找不到 sp.Skeleton 组件`);
}
const { nodeId: targetId } = resolveNode(prefabData, targetSelector, 'add-spine-socket target');
if (!Array.isArray(skeleton._sockets)) skeleton._sockets = [];
for (const socketRef of skeleton._sockets) {
if (!socketRef || typeof socketRef.__id__ !== 'number') continue;
const socket = elements[socketRef.__id__];
if (socket && socket.__type__ === 'sp.Skeleton.SpineSocket' && socket.path === path) {
socket.target = { __id__: targetId };
return nodeId;
}
}
const socketId = elements.length;
elements.push(makeSpineSocket({ path, targetId }));
skeleton._sockets.push({ __id__: socketId });
return nodeId;
}
module.exports = { execAddSpineSocket };
+17
View File
@@ -391,6 +391,22 @@ function makeSpSkeleton(opts) {
return obj;
}
/**
* 构造 sp.Skeleton.SpineSocket 裸对象。
* @param {object} opts
* @param {string} opts.path - Spine bone/slot path,例如 root/zk/tou2
* @param {number} opts.targetId - 绑定的 cc.Node __id__
* @returns {object}
*/
function makeSpineSocket(opts) {
const { path, targetId } = opts;
return {
__type__: 'sp.Skeleton.SpineSocket',
path,
target: ref(targetId),
};
}
// ─────────────────────────────────────────────
// cc.PrefabInfo / cc.CompPrefabInfo
// ─────────────────────────────────────────────
@@ -469,6 +485,7 @@ module.exports = {
makeLabel,
makeWidget,
makeSpSkeleton,
makeSpineSocket,
// Prefab 元信息
makePrefabInfo,
makeCompPrefabInfo,
+1
View File
@@ -360,6 +360,7 @@ stub 节点(嵌套 prefab 实例)在 prefab JSON 里 `_name = ""`(空字
| op | 参数 | 说明 |
|---|---|---|
| `add-node` | `parent`, `node: {name, lpos?, active?, components?, width?, height?, anchor?}` | 新增 cc.Nodeparent 是 stub 时走 mountedChildren。`components: ["UITransform"]` 自动建 UITransform |
| `add-spine-socket` | `node`, `path`, `target` | 给普通节点上的 `sp.Skeleton` 新增 / 更新 `sp.Skeleton.SpineSocket``path` 是 Spine socket path(如 `root/zk/tou2`),`target` 是绑定节点;同 path 幂等更新 target,不重复追加 |
| `remove-node` | `target` | 从父节点移除引用;元素本身保留(orphan),保持其他 `__id__` 稳定 |
| `sync-nested-roots` | (无) | 重建根 `PrefabInfo.nestedPrefabInstanceRoots`,剔除「删了一半」残留的悬空嵌套实例根(节点 `_parent` 已移除但根登记残留 → 残留嵌套 prefab 的 asset 仍被当依赖加载,运行时 404)。只重写该数组,不删 elements、不动其他 `__id__`、不产生 null 槽;被孤立的残留对象成为不可达 orphan。复用 remove-node 内部同名逻辑 |
| `clone-node` | `source`, `parent`, `name` | 深拷贝整棵子树,分配新 `__id__` + 新 fileId |