mirror of
https://github.com/HappyLifeOk/cc-3-8-x-mcp.git
synced 2026-06-10 17:56:47 +00:00
14c5b00f14
Cocos Creator 3.8.x MCP bridge extension with a built-in offline CLI. Components: - Editor extension: in-process MCP server exposing scene / asset-db / preview / local / editor-process-control tools - stdio router: aggregates multiple editor instances on one machine, with shortName dedup - offline CLI (cocos-mcp-cli): headless prefab read/write + a wrapper around the Cocos CLI build Pure Node.js, zero third-party dependencies. Licensed under Apache-2.0.
98 lines
3.0 KiB
JavaScript
98 lines
3.0 KiB
JavaScript
// ============================================================
|
||
// 确定性 UUID / fileId 生成器(纯 CJS,零三方依赖)
|
||
// 与 tools/fgui2cc3/src/utils/DeterministicId.ts byte-for-byte 对齐
|
||
// ============================================================
|
||
|
||
'use strict';
|
||
|
||
const { createHash } = require('node:crypto');
|
||
|
||
/**
|
||
* 基于种子字符串生成确定性 UUID v4 格式
|
||
* 使用 SHA-256 哈希前 16 字节,设置 version=4 和 variant=RFC4122
|
||
* @param {string} seed
|
||
* @returns {string}
|
||
*/
|
||
function deterministicUUID(seed) {
|
||
const hash = createHash('sha256').update(seed).digest();
|
||
// 设置 version (4) 和 variant (RFC 4122)
|
||
hash[6] = (hash[6] & 0x0f) | 0x40;
|
||
hash[8] = (hash[8] & 0x3f) | 0x80;
|
||
const hex = hash.subarray(0, 16).toString('hex');
|
||
return [
|
||
hex.slice(0, 8),
|
||
hex.slice(8, 12),
|
||
hex.slice(12, 16),
|
||
hex.slice(16, 20),
|
||
hex.slice(20, 32),
|
||
].join('-');
|
||
}
|
||
|
||
/**
|
||
* 基于种子字符串生成确定性 fileId(CC3 使用 base64 编码的 16 字节)
|
||
* @param {string} seed
|
||
* @returns {string}
|
||
*/
|
||
function deterministicFileId(seed) {
|
||
const hash = createHash('sha256').update(seed).digest();
|
||
return hash.subarray(0, 16).toString('base64').replace(/=+$/, '');
|
||
}
|
||
|
||
/**
|
||
* 创建一个带自增计数器的 fileId 生成器
|
||
* 同一组件内每个节点/组件使用递增的序号保证唯一性和稳定性
|
||
* @param {string} baseSeed
|
||
* @returns {() => string}
|
||
*/
|
||
function createFileIdGenerator(baseSeed) {
|
||
let counter = 0;
|
||
return () => deterministicFileId(`${baseSeed}#fid#${counter++}`);
|
||
}
|
||
|
||
// ============================================================
|
||
// Cocos Creator 压缩 classId 编解码
|
||
// 标准 base64(A-Z a-z 0-9 + /),前 5 hex 保留,每 3 hex → 2 base64
|
||
// 产物格式:23 字符 = 5 hex + 18 base64
|
||
// ============================================================
|
||
|
||
const _BASE64_STD = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
|
||
|
||
/**
|
||
* 把 32/36 位 uuid 压缩为 Cocos ccclass id(23 字符)。
|
||
*
|
||
* @param {string} uuid 标准 uuid(含或不含 dash)
|
||
* @returns {string} 23 字符压缩 classId
|
||
*/
|
||
function compressUuid(uuid) {
|
||
if (typeof uuid !== 'string') {
|
||
throw new Error(`compressUuid: uuid 必须是字符串,收到 ${typeof uuid}`);
|
||
}
|
||
const hex = uuid.replace(/-/g, '').toLowerCase();
|
||
if (!/^[0-9a-f]{32}$/.test(hex)) {
|
||
throw new Error(`compressUuid: 输入不是合法 uuid:${uuid}`);
|
||
}
|
||
let out = hex.slice(0, 5);
|
||
for (let i = 5; i < 32; i += 3) {
|
||
const code = parseInt(hex.substr(i, 3), 16);
|
||
out += _BASE64_STD[(code >> 6) & 0x3f];
|
||
out += _BASE64_STD[code & 0x3f];
|
||
}
|
||
return out;
|
||
}
|
||
|
||
/**
|
||
* 判断字符串是否是 Cocos 压缩 classId 格式(23 字符,前 5 hex + 后 18 base64)。
|
||
* 不做语义合法性校验(不查 classId 是否对应真实类)。
|
||
*/
|
||
function isCompressedClassId(str) {
|
||
return typeof str === 'string' && /^[0-9a-f]{5}[A-Za-z0-9+/]{18}$/.test(str);
|
||
}
|
||
|
||
module.exports = {
|
||
deterministicUUID,
|
||
deterministicFileId,
|
||
createFileIdGenerator,
|
||
compressUuid,
|
||
isCompressedClassId,
|
||
};
|