You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
AdLike2048/preview-template/workbench/js/agent-serialize.js

362 lines
14 KiB

3 days ago
(function (global) {
'use strict';
var WorkbenchAgent = global.PWAgent;
var U = global.PWAgentUtil;
var nodeId = U && U.nodeId;
if (!WorkbenchAgent) {
console.error('[调试工作台] agent-serialize 需先加载 agent-core');
return;
}
if (!nodeId) {
console.error('[调试工作台] agent-serialize 需先加载 agent-util');
return;
}
var COMP_PROP_SKIP = {
_name: 1, _objFlags: 1, __scriptAsset: 1, node: 1, _enabled: 1,
__prefab: 1, __editorExtras__: 1, uuid: 1, name: 1, enabled: 1,
};
/**
* 读取 CCClass 属性元数据(displayName / type / enumList / visible)。
* @param {Function} ctor
* @returns {object}
*/
function getCtorAttrs(ctor) {
if (!ctor) return {};
if (ctor.__attrs__ && typeof ctor.__attrs__ === 'object') return ctor.__attrs__;
return {};
}
/**
* @param {object} attrs
* @param {string} key
* @param {string} name
* @returns {*}
*/
function attrOf(attrs, key, name) {
return attrs[key + '$_$' + name];
}
/**
* 列出组件应展示的属性名(对齐编辑器:走 __props__,隐藏 _ 开头与 visible:false)。
* @param {Function} ctor
* @returns {string[]}
*/
function listCompPropKeys(ctor) {
var attrs = getCtorAttrs(ctor);
var raw = (ctor && (ctor.__props__ || ctor.__values__)) || [];
var out = [];
for (var i = 0; i < raw.length; i++) {
var key = raw[i];
if (!key || COMP_PROP_SKIP[key]) continue;
if (key.charAt(0) === '_' || key.indexOf('__') === 0) continue;
if (attrOf(attrs, key, 'visible') === false) continue;
out.push(key);
}
return out;
}
/**
* 将运行时值序列化为 Inspector DTO。
* @param {*} val
* @param {object} attrs
* @param {string} key
* @param {object} cc
* @returns {{type:string, value:*, enumOptions?:Array, readOnly?:boolean}}
*/
function serializeFieldValue(val, attrs, key, cc) {
var enumList = attrOf(attrs, key, 'enumList');
var typeTag = attrOf(attrs, key, 'type');
if (enumList && enumList.length) {
return {
type: 'enum',
value: val,
enumOptions: enumList.map(function (it) {
return { name: String(it.name), value: it.value };
}),
};
}
if (typeTag === 'Enum') {
return { type: 'number', value: typeof val === 'number' ? val : Number(val) || 0 };
}
var t = typeof val;
if (t === 'boolean' || t === 'string') return { type: t, value: val };
if (t === 'number') return { type: 'number', value: isFinite(val) ? val : 0 };
if (val == null) {
var ctorHint = attrOf(attrs, key, 'ctor');
var hintName = ctorHint && (ctorHint.name || ctorHint.prototype && ctorHint.prototype.__classname__);
if (hintName || typeTag === 'Object') {
return {
type: 'ref',
readOnly: true,
value: { kind: hintName || 'Object', name: '(空)', uuid: '' },
};
}
return { type: 'string', value: '' };
}
if (cc && cc.Color && val instanceof cc.Color) {
return {
type: 'color',
value: { r: val.r | 0, g: val.g | 0, b: val.b | 0, a: val.a != null ? (val.a | 0) : 255 },
};
}
// Size
if (typeof val.width === 'number' && typeof val.height === 'number' && val.x === undefined) {
return { type: 'size', value: { width: val.width, height: val.height } };
}
// Vec2 / Vec3
if (typeof val.x === 'number' && typeof val.y === 'number') {
if (typeof val.z === 'number') {
return { type: 'vec3', value: { x: val.x, y: val.y, z: val.z } };
}
return { type: 'vec2', value: { x: val.x, y: val.y } };
}
// Asset / Node / Component 引用
if (typeof val === 'object') {
var kind = (val.constructor && val.constructor.name) || 'Object';
var name = '';
if (val.name != null) name = String(val.name);
else if (val.node && val.node.name) name = String(val.node.name);
var uuid = val._uuid || val.uuid || '';
return {
type: 'ref',
readOnly: true,
value: { kind: kind, name: name || kind, uuid: uuid ? String(uuid) : '' },
};
}
return { type: 'string', value: String(val) };
}
/**
* 提取单个组件的 Inspector 字段列表。
* @param {object} comp
* @param {number} index
* @param {object} cc
* @returns {{name:string, index:number, fields:Array}}
*/
function extractCompFields(comp, index, cc) {
var ctor = comp.constructor;
var cname = (cc && cc.js && typeof cc.js.getClassName === 'function')
? (cc.js.getClassName(ctor) || ctor.name)
: (ctor && ctor.name) || 'Component';
var attrs = getCtorAttrs(ctor);
var keys = listCompPropKeys(ctor);
// 无元数据时有限回退
if (!keys.length) {
var own = Object.keys(comp);
for (var o = 0; o < own.length && keys.length < 24; o++) {
var ok = own[o];
if (!ok || ok.charAt(0) === '_' || COMP_PROP_SKIP[ok]) continue;
var ot = typeof comp[ok];
if (ot === 'number' || ot === 'boolean' || ot === 'string') keys.push(ok);
}
if (!keys.length) {
console.warn('[Agent] 组件无 CCClass 属性元数据', cname);
}
}
var fields = [];
var max = 80;
for (var i = 0; i < keys.length && fields.length < max; i++) {
var key = keys[i];
var label = attrOf(attrs, key, 'displayName') || key;
var raw;
try { raw = comp[key]; } catch (e) { continue; }
var ser = serializeFieldValue(raw, attrs, key, cc);
fields.push({
key: key,
label: label,
type: ser.type,
value: ser.value,
enumOptions: ser.enumOptions || null,
readOnly: !!ser.readOnly,
});
}
return { name: cname, index: index, fields: fields };
}
WorkbenchAgent.prototype._getNodeProps = function (uuid) {
var node = this._findNode(uuid);
if (!node) return null;
var cc = this.cc;
var p = node.getPosition ? node.getPosition() : node.position;
var e = node.eulerAngles || { x: 0, y: 0, z: 0 };
var s = node.getScale ? node.getScale() : node.scale;
var comps = [];
if (node.components) {
for (var i = 0; i < node.components.length; i++) {
var comp = node.components[i];
if (!comp || !comp.constructor) continue;
comps.push(extractCompFields(comp, i, cc));
}
}
return {
uuid: nodeId(node),
name: node.name || '',
active: !!node.active,
layer: node.layer,
pos: { x: p.x, y: p.y, z: p.z },
euler: { x: e.x, y: e.y, z: e.z },
scale: { x: s.x, y: s.y, z: s.z },
comps: comps,
};
};
WorkbenchAgent.prototype._setNodeProp = function (uuid, fid, value) {
var node = this._findNode(uuid);
if (!node || !fid) return false;
var cc = this.cc;
try {
if (fid === 'name') node.name = String(value);
else if (fid === 'active') node.active = !!value;
else if (fid === 'layer') node.layer = Number(value) || 0;
else if (fid === 'px' || fid === 'py' || fid === 'pz') {
var p = node.getPosition ? node.getPosition() : node.position;
var x = fid === 'px' ? Number(value) : p.x;
var y = fid === 'py' ? Number(value) : p.y;
var z = fid === 'pz' ? Number(value) : p.z;
if (typeof node.setPosition === 'function') node.setPosition(x, y, z);
else if (cc && cc.Vec3) node.position = new cc.Vec3(x, y, z);
} else if (fid === 'rx' || fid === 'ry' || fid === 'rz') {
var e = node.eulerAngles || { x: 0, y: 0, z: 0 };
var ex = fid === 'rx' ? Number(value) : e.x;
var ey = fid === 'ry' ? Number(value) : e.y;
var ez = fid === 'rz' ? Number(value) : e.z;
if (typeof node.setRotationFromEuler === 'function') node.setRotationFromEuler(ex, ey, ez);
else if (cc && cc.Vec3) node.eulerAngles = new cc.Vec3(ex, ey, ez);
} else if (fid === 'sx' || fid === 'sy' || fid === 'sz') {
var sc = node.getScale ? node.getScale() : node.scale;
var sx = fid === 'sx' ? Number(value) : sc.x;
var sy = fid === 'sy' ? Number(value) : sc.y;
var sz = fid === 'sz' ? Number(value) : sc.z;
if (typeof node.setScale === 'function') node.setScale(sx, sy, sz);
else if (cc && cc.Vec3) node.scale = new cc.Vec3(sx, sy, sz);
} else if (fid.indexOf('c') === 0 && fid.indexOf('_') > 0) {
return this._setCompProp(node, fid, value);
} else {
return false;
}
return true;
} catch (err) {
console.warn('[Agent] set prop', fid, err);
return false;
}
};
/**
* 写回组件字段。fid: c{index}_{key}[_axis]
* @param {object} node
* @param {string} fid
* @param {*} value
* @returns {boolean}
*/
WorkbenchAgent.prototype._setCompProp = function (node, fid, value) {
var m = /^c(\d+)_(.+)$/.exec(fid);
if (!m) return false;
var ci = parseInt(m[1], 10);
var rest = m[2];
var comp = node.components && node.components[ci];
if (!comp) return false;
var cc = this.cc;
var axis = '';
var key = rest;
var axisMatch = /^(.*)_(x|y|z|w|h|r|g|b|a|hex)$/.exec(rest);
if (axisMatch) {
// 优先整键命中(字段名本身含下划线时仍以整键为准)
if (!(rest in comp) && (axisMatch[1] in comp || hasCompProp(comp, axisMatch[1]))) {
key = axisMatch[1];
axis = axisMatch[2];
}
}
if (axis) {
return setCompAxisValue(comp, key, axis, value, cc);
}
var attrs = getCtorAttrs(comp.constructor);
if (attrOf(attrs, key, 'type') === 'Object' || attrOf(attrs, key, 'ctor')) {
// 引用只读
return false;
}
var cur;
try { cur = comp[key]; } catch (e1) { return false; }
if (cur && typeof cur === 'object' && !(typeof cur === 'boolean')) {
// 未带轴的对象写入拒绝(避免覆盖引用)
if (typeof cur.x === 'number' || typeof cur.width === 'number' || (cc && cc.Color && cur instanceof cc.Color)) {
return false;
}
}
try {
if (typeof cur === 'boolean') comp[key] = !!value;
else if (typeof cur === 'number') comp[key] = Number(value);
else if (typeof cur === 'string') comp[key] = String(value);
else if (attrOf(attrs, key, 'enumList')) comp[key] = Number(value);
else comp[key] = value;
return true;
} catch (e2) {
console.warn('[Agent] set comp prop', key, e2);
return false;
}
};
/**
* @param {object} comp
* @param {string} key
* @returns {boolean}
*/
function hasCompProp(comp, key) {
if (!comp || !key) return false;
if (key in comp) return true;
var ctor = comp.constructor;
var props = (ctor && (ctor.__props__ || ctor.__values__)) || [];
return props.indexOf(key) >= 0;
}
/**
* @param {object} comp
* @param {string} key
* @param {string} axis
* @param {*} value
* @param {object} cc
* @returns {boolean}
*/
function setCompAxisValue(comp, key, axis, value, cc) {
var cur;
try { cur = comp[key]; } catch (e) { return false; }
if (axis === 'hex') {
var hex = String(value).replace(/^#/, '');
if (hex.length < 6) return false;
var r = parseInt(hex.slice(0, 2), 16);
var g = parseInt(hex.slice(2, 4), 16);
var b = parseInt(hex.slice(4, 6), 16);
var a = hex.length >= 8 ? parseInt(hex.slice(6, 8), 16) : 255;
if (cc && cc.Color) {
comp[key] = new cc.Color(r, g, b, a);
return true;
}
if (cur && typeof cur.r === 'number') {
cur.r = r; cur.g = g; cur.b = b; cur.a = a;
return true;
}
return false;
}
var num = Number(value);
if (!isFinite(num)) return false;
if (!cur || typeof cur !== 'object') return false;
if (axis === 'w' && typeof cur.width === 'number') { cur.width = num; return true; }
if (axis === 'h' && typeof cur.height === 'number') { cur.height = num; return true; }
if ((axis === 'x' || axis === 'y' || axis === 'z') && typeof cur[axis] === 'number') {
cur[axis] = num;
// 触发 setter(部分引擎属性需整体赋值)
try { comp[key] = cur; } catch (e2) { /* 就地改写已足够 */ }
return true;
}
if ((axis === 'r' || axis === 'g' || axis === 'b' || axis === 'a') && typeof cur[axis] === 'number') {
cur[axis] = num;
try { comp[key] = cur; } catch (e3) { /* */ }
return true;
}
return false;
}
})(window);