Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 12 additions & 12 deletions e2e/mcp/api/component.e2e.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ describe('MCP Component API', () => {
const addResult = await mcpClient.callTool('scene-add-component', {
addComponentInfo: {
nodePath: testNodePath,
component: 'cc.Label',
componentNameOrUuidOrUrl: 'cc.Label',
},
});
expect(addResult.code).toBe(200);
Expand All @@ -97,7 +97,7 @@ describe('MCP Component API', () => {
const addResult = await mcpClient.callTool('scene-add-component', {
addComponentInfo: {
nodePath: testNodePath,
component: 'cc.Label',
componentNameOrUuidOrUrl: 'cc.Label',
},
});
expect(addResult.code).toBe(200);
Expand All @@ -108,7 +108,7 @@ describe('MCP Component API', () => {

// 查询组件
const queryResult = await mcpClient.callTool('scene-query-component', {
component: { path: componentPath }
component: { pathOrUuidOrUrl: componentPath }
});
expect(queryResult.code).toBe(200);
expect(queryResult.data).toBeDefined();
Expand All @@ -124,7 +124,7 @@ describe('MCP Component API', () => {
const addResult = await mcpClient.callTool('scene-add-component', {
addComponentInfo: {
nodePath: testNodePath,
component: 'cc.Label',
componentNameOrUuidOrUrl: 'cc.Label',
},
});
expect(addResult.code).toBe(200);
Expand All @@ -135,7 +135,7 @@ describe('MCP Component API', () => {

// 查询组件初始属性
const queryResult = await mcpClient.callTool('scene-query-component', {
component: { path: componentPath }
component: { pathOrUuidOrUrl: componentPath }
});
expect(queryResult.code).toBe(200);
expect(queryResult.data).toBeDefined();
Expand All @@ -155,7 +155,7 @@ describe('MCP Component API', () => {

// 验证属性已更改
const queryAfterSet = await mcpClient.callTool('scene-query-component', {
component: { path: componentPath }
component: { pathOrUuidOrUrl: componentPath }
});
expect(queryAfterSet.code).toBe(200);
expect(queryAfterSet.data).toBeDefined();
Expand All @@ -168,7 +168,7 @@ describe('MCP Component API', () => {
const addResult = await mcpClient.callTool('scene-add-component', {
addComponentInfo: {
nodePath: testNodePath,
component: 'cc.Label',
componentNameOrUuidOrUrl: 'cc.Label',
},
});
expect(addResult.code).toBe(200);
Expand All @@ -178,13 +178,13 @@ describe('MCP Component API', () => {

// 删除组件
const deleteResult = await mcpClient.callTool('scene-delete-component', {
component: { path: componentPath }
component: { pathOrUuidOrUrl: componentPath }
});
expect(deleteResult.code).toBe(200);

// 验证组件已删除 - 查询应该返回null或失败
const queryAfterDelete = await mcpClient.callTool('scene-query-component', {
component: { path: componentPath }
component: { pathOrUuidOrUrl: componentPath }
});
// 组件删除后查询应该失败或返回null
expect(queryAfterDelete.code).not.toBe(200);
Expand All @@ -201,7 +201,7 @@ describe('MCP Component API', () => {
const addResult = await mcpClient.callTool('scene-add-component', {
addComponentInfo: {
nodePath: testNodePath,
component: componentType,
componentNameOrUuidOrUrl: componentType,
},
});
expect(addResult.code).toBe(200);
Expand All @@ -212,7 +212,7 @@ describe('MCP Component API', () => {

// 验证组件已添加
const queryResult = await mcpClient.callTool('scene-query-component', {
component: { path: addResult.data.path }
component: { pathOrUuidOrUrl: addResult.data.path }
});
expect(queryResult.code).toBe(200);
expect(queryResult.data).toBeDefined();
Expand All @@ -223,7 +223,7 @@ describe('MCP Component API', () => {
// 清理添加的组件
for (const componentPath of addedComponents) {
await mcpClient.callTool('scene-delete-component', {
component: { path: componentPath }
component: { pathOrUuidOrUrl: componentPath }
});
}
});
Expand Down
6 changes: 3 additions & 3 deletions src/api/scene/component-schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,17 @@ import { SchemaCompPrefabInfo } from './prefab-info-schema';
export const SchemaAddComponentInfo = z.object({
nodePath: z.string().describe('Node path'), // 节点路径
//component: z.enum(Object.keys(globalComponentType) as [string, ...string[]]).describe('组件类型'),
component: z.string().describe('Component name, supports component name, component resource URL and UUID'), // 组件名称,支持组件名称、组件资源的 URL 与 UUID
componentNameOrUuidOrUrl: z.string().describe('Component name, supports component name, component resource URL and UUID'), // 组件名称,支持组件名称、组件资源的 URL 与 UUID
}).describe('Information for adding a component'); // 添加组件的信息

// Remove component // 移除组件
export const SchemaRemoveComponent = z.object({
path: z.string().describe('Path of the component, including node path'), // 组件的路径,包含节点路径
pathOrUuidOrUrl: z.string().describe('Path, UUID or URL of the component'), // 组件的路径、uuid 或 url
}).describe('Information required to remove a component'); // 移除组件需要的信息

// Query component // 查询组件
export const SchemaQueryComponent = z.object({
path: z.string().describe('Path of the component, including node path'), // 组件的路径,包含节点路径
pathOrUuidOrUrl: z.string().describe('Path, UUID or URL of the component'), // 组件的路径、uuid 或 url
}).describe('Information required to query a component'); // 查询组件需要的信息

// Vec2
Expand Down
8 changes: 4 additions & 4 deletions src/api/scene/component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import {

import { description, param, result, title, tool } from '../decorator/decorator.js';
import { COMMON_STATUS, CommonResultType } from '../base/schema-base';
import { Scene, ISetPropertyOptions } from '../../core/scene';
import { Scene, ISetPropertyOptions, IComponent } from '../../core/scene';

export class ComponentApi {

Expand All @@ -30,7 +30,7 @@ export class ComponentApi {
@result(SchemaComponentResult)
async addComponent(@param(SchemaAddComponentInfo) addComponentInfo: TAddComponentInfo): Promise<CommonResultType<TComponentResult>> {
try {
const component = await Scene.addComponent({ nodePath: addComponentInfo.nodePath, component: addComponentInfo.component });
const component = await Scene.addComponent({ nodePathOrUuid: addComponentInfo.nodePath, component: addComponentInfo.componentNameOrUuidOrUrl });
return {
code: COMMON_STATUS.SUCCESS,
data: component
Expand Down Expand Up @@ -76,11 +76,11 @@ export class ComponentApi {
try {
const componentInfo = await Scene.queryComponent(component);
if (!componentInfo) {
throw new Error(`component not fount at path ${component.path}`);
throw new Error(`component not found: ${component.pathOrUuidOrUrl}`);
}
return {
code: COMMON_STATUS.SUCCESS,
data: componentInfo
data: componentInfo as IComponent
};
} catch (e) {
return {
Expand Down
62 changes: 61 additions & 1 deletion src/core/scene/@types/public.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,72 @@ export interface Mat4 {

export type IPropertyValueType = IProperty | IProperty[] | null | undefined | number | boolean | string | Vec4 | Vec3 | Vec2 | Mat4 | any | Array<unknown>

export interface IPropertyGroupOptions {
id: string // 默认 'default'
name: string,
displayOrder: number, // 默认 Infinity, 排在最后面
style: string // 默认为 'tab'
}

export type IPropertyLock = {
default: number;
message: string
};

/**
* 组件的 dump 数据,以 IProperty 格式编码组件信息
* 与 IComponent 不同,所有属性(包括 uuid, name, enabled)都通过 encodeObject 编码为 IProperty
*/
export interface IProperty {
value: { [key: string]: IPropertyValueType } | IPropertyValueType;
default?: any; // 默认值

// 多选节点之后,这里存储多个数据,用于自行判断多选后的显示效果,无需更新该数据
values?: ({ [key: string]: IPropertyValueType } | IPropertyValueType)[];

lock?: { [key in keyof Vec4]?: IPropertyLock };

cid?: string;
type?: string;
ui?: { name: string; data?: any }; // 是否用指定的 UI 组件,name 是组件的名称
readonly?: boolean;
visible?: boolean;
name?: string;

elementTypeData?: IProperty; // 数组里的数据的默认值 dump

path?: string; // 数据的搜索路径,这个是由使用方填充的

isArray?: boolean;
invalid?: boolean;
extends?: string[]; // 继承链
displayName?: string; // 显示到界面上的名字
displayOrder?: number; // 显示排序
help?: string; // 帮助文档的 url 地址
group?: IPropertyGroupOptions; // tab
tooltip?: string; // 提示文本
editor?: any; // 组件上定义的编辑器数据
animatable?: boolean; // 是否可以在动画中编辑
radioGroup?: boolean; // 是否渲染为 RadioGroup

// Enum
enumList?: any[]; // enum 类型的 list 选项数组

bitmaskList?: any[];

// Number
min?: number; // 数值类型的最小值
max?: number; // 数值类型的最大值
step?: number; // 数值类型的步进值
slide?: boolean; // 数组是否显示为滑块
unit?: string; // 显示的单位
radian?: boolean; // 标识是否为角度

// Label
multiline?: boolean; // 字符串是否允许换行
// nullable?: boolean; 属性是否允许为空

optionalTypes?: string[]; // 对属性是 object 且是可变类型的数据的支持,比如 render-pipeline

userData?: { [key: string]: any }; // 用户透传的数据
}
}
79 changes: 67 additions & 12 deletions src/core/scene/common/component.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import type { Component } from 'cc';
import type { IPropertyValueType } from '../@types/public';
import type { Component, Node } from 'cc';
import type { IPropertyValueType, IProperty } from '../@types/public';
import type { IServiceEvents } from '../scene-process/service/core';
import type { ICompPrefabInfo } from './prefab';
import type { IChangeNodeOptions } from './node';
import type { IChangeNodeOptions, INodeEvents } from './node';

/**
* 代表一个组件
Expand All @@ -24,26 +24,36 @@ export interface IComponent extends IComponentIdentifier {
prefab: ICompPrefabInfo | null;
}

export interface IComponentForPinK extends IProperty {
value: {
enabled: IPropertyValueType;
uuid: IPropertyValueType;
name: IPropertyValueType;
} & Record<string, IPropertyValueType>;
mountedRoot?: string;
}

/**
* 创建组件
* 创建/添加组件
*/
export interface IAddComponentOptions {
nodePath: string;// 组件路径
component: string;// 组件注册到ccclass里的类名
nodePathOrUuid: string;// 节点路径或 uuid
component: string;// 组件类名
}

/**
* 删除组件
*/
export interface IRemoveComponentOptions {
path: string;// 组件的路径,不包含节点路径
pathOrUuidOrUrl: string;// 组件的路径、uuid 或 url
}

/**
* 查询组件
*/
export interface IQueryComponentOptions {
path: string;// 组件的路径,不包含节点路径
pathOrUuidOrUrl: string;// 组件的路径、uuid 或 url
isFull?: boolean; // 默认为false,cli默认输出简单的信息,pink设置为true,返回详细的信息,结构不一样
}

/**
Expand All @@ -53,21 +63,40 @@ export interface ISetPropertyOptions {
componentPath: string; // 修改属性的对象的 uuid
// key: string; // 属性的 key
properties: {
[key: string]: null | undefined | number | boolean | string | Object | Array<unknown>;
[key: string]: null | undefined | number | boolean | string | object | Array<unknown>;
}; // 属性 dump 出来的数据
record?: boolean;// 是否记录undo
}

export interface ISetPropertyOptionsForPink {
uuid: string; // 修改属性的对象的 uuid
path: string; // 属性挂载对象的搜索路径
// key: string; // 属性的 key
dump: IProperty; // 属性 dump 出来的数据
record?: boolean;// 是否记录undo
}


/**
* 执行组件方法选项
*/
export interface IExecuteComponentMethodOptions {
uuid: string;
name: string;
args: any[];
}

/**
* 场景事件类型
*/
export interface IComponentEvents {
export interface IComponentEvents extends INodeEvents {
'component:add': [Component];
'component:before-remove': [Component];
'component:remove': [Component];
'component:set-property': [Component, IChangeNodeOptions];
'component:added': [Component];
'component:removed': [Component];
'component:before-add-component': [string, Node];
}

export interface IPublicComponentService extends Omit<IComponentService, keyof IServiceEvents |
Expand All @@ -80,10 +109,17 @@ export interface IPublicComponentService extends Omit<IComponentService, keyof I
*/
export interface IComponentService extends IServiceEvents {
/**
* 创建组件
* 添加组件
* @param params
*/
addComponent(params: IAddComponentOptions): Promise<IComponent>;

/**
* 创建组件
* @param params
*/
createComponent(params: IAddComponentOptions): Promise<boolean> ;

/**
* 删除组件
* @param params
Expand All @@ -94,15 +130,34 @@ export interface IComponentService extends IServiceEvents {
* @param params
*/
setProperty(params: ISetPropertyOptions): Promise<boolean>;

/**
* 设置组件属性,pink专属接口,因为结构与ISetPropertyOptions是不同的
* @param params
*/
setPropertyForPink(uuid: string, path: string, dump: IProperty, record?: boolean): Promise<boolean>;

/**
* 查询组件
*/
queryComponent(params: IQueryComponentOptions): Promise<IComponent | null>;
queryComponent(params: IQueryComponentOptions): Promise<IComponent | IComponentForPinK | null>;

/**
* 获取所有组件名,包含内置与自定义组件
*/
queryAllComponent(): Promise<string[]>

// pink 相关接口
/**
* 复位组件
*/
resetComponent(params: IQueryComponentOptions): Promise<boolean>;

/**
* 执行组件方法
*/
executeComponentMethod(options: IExecuteComponentMethodOptions): Promise<boolean>

// 不对外接口

init(): void;
Expand Down
Loading
Loading