Feature/runtime cdn and plugin loader (#240)

* feat(ui): 完善 UI 布局系统和编辑器可视化工具

* refactor: 移除 ModuleRegistry,统一使用 PluginManager 插件系统

* fix: 修复 CodeQL 警告并提升测试覆盖率

* refactor: 分离运行时入口点,解决 runtime bundle 包含 React 的问题

* fix(ci): 添加 editor-core 和 editor-runtime 到 CI 依赖构建步骤

* docs: 完善 ServiceContainer 文档,新增 Symbol.for 模式和 @InjectProperty 说明

* fix(ci): 修复 type-check 失败问题

* fix(ci): 修复类型检查失败问题

* fix(ci): 修复类型检查失败问题

* fix(ci): behavior-tree 构建添加 @tauri-apps 外部依赖

* fix(ci): behavior-tree 添加 @tauri-apps/plugin-fs 类型依赖

* fix(ci): platform-web 添加缺失的 behavior-tree 依赖

* fix(lint): 移除正则表达式中不必要的转义字符
This commit is contained in:
YHH
2025-11-27 20:42:46 +08:00
committed by GitHub
parent 71869b1a58
commit 107439d70c
367 changed files with 10661 additions and 12473 deletions

View File

@@ -7,7 +7,6 @@ interface ErrorDialogData {
}
interface DialogState {
showPluginManager: boolean;
showProfiler: boolean;
showPortManager: boolean;
showSettings: boolean;
@@ -16,7 +15,6 @@ interface DialogState {
errorDialog: ErrorDialogData | null;
confirmDialog: ConfirmDialogData | null;
setShowPluginManager: (show: boolean) => void;
setShowProfiler: (show: boolean) => void;
setShowPortManager: (show: boolean) => void;
setShowSettings: (show: boolean) => void;
@@ -28,7 +26,6 @@ interface DialogState {
}
export const useDialogStore = create<DialogState>((set) => ({
showPluginManager: false,
showProfiler: false,
showPortManager: false,
showSettings: false,
@@ -37,7 +34,6 @@ export const useDialogStore = create<DialogState>((set) => ({
errorDialog: null,
confirmDialog: null,
setShowPluginManager: (show) => set({ showPluginManager: show }),
setShowProfiler: (show) => set({ showProfiler: show }),
setShowPortManager: (show) => set({ showPortManager: show }),
setShowSettings: (show) => set({ showSettings: show }),
@@ -47,7 +43,6 @@ export const useDialogStore = create<DialogState>((set) => ({
setConfirmDialog: (data) => set({ confirmDialog: data }),
closeAllDialogs: () => set({
showPluginManager: false,
showProfiler: false,
showPortManager: false,
showSettings: false,

View File

@@ -1,28 +1,69 @@
import type { EditorPluginManager } from '@esengine/editor-core';
import { SceneInspectorPlugin } from '../../plugins/SceneInspectorPlugin';
import { ProfilerPlugin } from '../../plugins/ProfilerPlugin';
import { EditorAppearancePlugin } from '../../plugins/EditorAppearancePlugin';
import { GizmoPlugin } from '../../plugins/GizmoPlugin';
import { TilemapEditorPlugin } from '@esengine/tilemap-editor';
import { UIEditorPlugin } from '@esengine/ui-editor';
/**
* 插件安装器
* Plugin Installer
*/
import type { PluginManager } from '@esengine/editor-core';
// 内置插件
import { GizmoPlugin } from '../../plugins/builtin/GizmoPlugin';
import { SceneInspectorPlugin } from '../../plugins/builtin/SceneInspectorPlugin';
import { ProfilerPlugin } from '../../plugins/builtin/ProfilerPlugin';
import { EditorAppearancePlugin } from '../../plugins/builtin/EditorAppearancePlugin';
import { PluginConfigPlugin } from '../../plugins/builtin/PluginConfigPlugin';
import { ProjectSettingsPlugin } from '../../plugins/builtin/ProjectSettingsPlugin';
// 统一模块插件CSS 已内联到 JS 中,导入时自动注入)
import { TilemapPlugin } from '@esengine/tilemap';
import { UIPlugin } from '@esengine/ui';
import { BehaviorTreePlugin } from '@esengine/behavior-tree';
export class PluginInstaller {
async installBuiltinPlugins(pluginManager: EditorPluginManager): Promise<void> {
const plugins = [
new GizmoPlugin(),
new SceneInspectorPlugin(),
new ProfilerPlugin(),
new EditorAppearancePlugin(),
new TilemapEditorPlugin(),
new UIEditorPlugin()
/**
* 安装所有内置插件
*/
async installBuiltinPlugins(pluginManager: PluginManager): Promise<void> {
// 内置编辑器插件
const builtinPlugins = [
{ name: 'GizmoPlugin', plugin: GizmoPlugin },
{ name: 'SceneInspectorPlugin', plugin: SceneInspectorPlugin },
{ name: 'ProfilerPlugin', plugin: ProfilerPlugin },
{ name: 'EditorAppearancePlugin', plugin: EditorAppearancePlugin },
{ name: 'PluginConfigPlugin', plugin: PluginConfigPlugin },
{ name: 'ProjectSettingsPlugin', plugin: ProjectSettingsPlugin },
];
for (const plugin of plugins) {
for (const { name, plugin } of builtinPlugins) {
if (!plugin || !plugin.descriptor) {
console.error(`[PluginInstaller] ${name} is invalid: missing descriptor`, plugin);
continue;
}
try {
await pluginManager.installEditor(plugin);
pluginManager.register(plugin);
} catch (error) {
console.error(`[PluginInstaller] Failed to install plugin ${plugin.name}:`, error);
console.error(`[PluginInstaller] Failed to register ${name}:`, error);
}
}
// 统一模块插件runtime + editor
const modulePlugins = [
{ name: 'TilemapPlugin', plugin: TilemapPlugin },
{ name: 'UIPlugin', plugin: UIPlugin },
{ name: 'BehaviorTreePlugin', plugin: BehaviorTreePlugin },
];
for (const { name, plugin } of modulePlugins) {
if (!plugin || !plugin.descriptor) {
console.error(`[PluginInstaller] ${name} is invalid: missing descriptor`, plugin);
continue;
}
try {
pluginManager.register(plugin);
} catch (error) {
console.error(`[PluginInstaller] Failed to register ${name}:`, error);
}
}
// All builtin plugins registered
}
}
}

View File

@@ -12,9 +12,11 @@ import {
LogService,
SettingsRegistry,
SceneManagerService,
SceneTemplateRegistry,
FileActionRegistry,
EntityCreationRegistry,
EditorPluginManager,
PluginManager,
IPluginManager,
InspectorRegistry,
IInspectorRegistry,
PropertyRendererRegistry,
@@ -37,6 +39,7 @@ import {
CircleColliderComponent,
AudioSourceComponent
} from '@esengine/ecs-components';
import { BehaviorTreeRuntimeComponent } from '@esengine/behavior-tree';
import { TauriFileAPI } from '../../adapters/TauriFileAPI';
import { DIContainer } from '../../core/di/DIContainer';
import { TypedEventBus } from '../../core/events/TypedEventBus';
@@ -80,7 +83,7 @@ export interface EditorServices {
settingsRegistry: SettingsRegistry;
sceneManager: SceneManagerService;
fileActionRegistry: FileActionRegistry;
pluginManager: EditorPluginManager;
pluginManager: PluginManager;
diContainer: DIContainer;
eventBus: TypedEventBus<EditorEventMap>;
commandRegistry: CommandRegistry;
@@ -106,15 +109,16 @@ export class ServiceRegistry {
// 注册标准组件到编辑器和核心注册表
// Register to both editor registry (for UI) and core registry (for serialization)
const standardComponents = [
{ name: 'TransformComponent', type: TransformComponent, editorName: 'Transform', category: 'components.category.core', description: 'components.transform.description' },
{ name: 'SpriteComponent', type: SpriteComponent, editorName: 'Sprite', category: 'components.category.rendering', description: 'components.sprite.description' },
{ name: 'SpriteAnimatorComponent', type: SpriteAnimatorComponent, editorName: 'SpriteAnimator', category: 'components.category.rendering', description: 'components.spriteAnimator.description' },
{ name: 'TextComponent', type: TextComponent, editorName: 'Text', category: 'components.category.rendering', description: 'components.text.description' },
{ name: 'CameraComponent', type: CameraComponent, editorName: 'Camera', category: 'components.category.rendering', description: 'components.camera.description' },
{ name: 'RigidBodyComponent', type: RigidBodyComponent, editorName: 'RigidBody', category: 'components.category.physics', description: 'components.rigidBody.description' },
{ name: 'BoxColliderComponent', type: BoxColliderComponent, editorName: 'BoxCollider', category: 'components.category.physics', description: 'components.boxCollider.description' },
{ name: 'CircleColliderComponent', type: CircleColliderComponent, editorName: 'CircleCollider', category: 'components.category.physics', description: 'components.circleCollider.description' },
{ name: 'AudioSourceComponent', type: AudioSourceComponent, editorName: 'AudioSource', category: 'components.category.audio', description: 'components.audioSource.description' }
{ name: 'TransformComponent', type: TransformComponent, editorName: 'Transform', category: 'components.category.core', description: 'components.transform.description', icon: 'Move3d' },
{ name: 'SpriteComponent', type: SpriteComponent, editorName: 'Sprite', category: 'components.category.rendering', description: 'components.sprite.description', icon: 'Image' },
{ name: 'SpriteAnimatorComponent', type: SpriteAnimatorComponent, editorName: 'SpriteAnimator', category: 'components.category.rendering', description: 'components.spriteAnimator.description', icon: 'Film' },
{ name: 'TextComponent', type: TextComponent, editorName: 'Text', category: 'components.category.rendering', description: 'components.text.description', icon: 'Type' },
{ name: 'CameraComponent', type: CameraComponent, editorName: 'Camera', category: 'components.category.rendering', description: 'components.camera.description', icon: 'Camera' },
{ name: 'RigidBodyComponent', type: RigidBodyComponent, editorName: 'RigidBody', category: 'components.category.physics', description: 'components.rigidBody.description', icon: 'Atom' },
{ name: 'BoxColliderComponent', type: BoxColliderComponent, editorName: 'BoxCollider', category: 'components.category.physics', description: 'components.boxCollider.description', icon: 'Square' },
{ name: 'CircleColliderComponent', type: CircleColliderComponent, editorName: 'CircleCollider', category: 'components.category.physics', description: 'components.circleCollider.description', icon: 'Circle' },
{ name: 'AudioSourceComponent', type: AudioSourceComponent, editorName: 'AudioSource', category: 'components.category.audio', description: 'components.audioSource.description', icon: 'Volume2' },
{ name: 'BehaviorTreeRuntimeComponent', type: BehaviorTreeRuntimeComponent, editorName: 'BehaviorTreeRuntime', category: 'components.category.ai', description: 'components.behaviorTreeRuntime.description', icon: 'GitBranch' }
];
for (const comp of standardComponents) {
@@ -123,7 +127,8 @@ export class ServiceRegistry {
name: comp.editorName,
type: comp.type,
category: comp.category,
description: comp.description
description: comp.description,
icon: comp.icon
});
// Register to core registry for serialization/deserialization
@@ -158,9 +163,8 @@ export class ServiceRegistry {
Core.services.registerInstance(ComponentActionRegistry, componentActionRegistry);
Core.services.registerInstance(ComponentInspectorRegistry, componentInspectorRegistry);
const pluginManager = new EditorPluginManager();
pluginManager.initialize(coreInstance, Core.services);
Core.services.registerInstance(EditorPluginManager, pluginManager);
const pluginManager = new PluginManager();
Core.services.registerInstance(IPluginManager, pluginManager);
const diContainer = new DIContainer();
const eventBus = new TypedEventBus<EditorEventMap>();
@@ -202,6 +206,10 @@ export class ServiceRegistry {
fieldEditorRegistry.register(new ColorFieldEditor());
fieldEditorRegistry.register(new AnimationClipsFieldEditor());
// 注册默认场景模板 - 创建默认相机
// Register default scene template - creates default camera
this.registerDefaultSceneTemplate();
return {
uiRegistry,
messageHub,
@@ -235,4 +243,31 @@ export class ServiceRegistry {
logService.addRemoteLog(level, message, timestamp, clientId);
}) as EventListener);
}
/**
* 注册默认场景模板
* Register default scene template with default entities
*/
private registerDefaultSceneTemplate(): void {
// 注册默认相机创建器
// Register default camera creator
SceneTemplateRegistry.registerDefaultEntity((scene) => {
// 检查是否已存在相机
// Check if camera already exists
const existingCameras = scene.entities.findEntitiesWithComponent(CameraComponent);
if (existingCameras.length > 0) {
return null;
}
// 创建默认相机实体
// Create default camera entity
const cameraEntity = scene.createEntity('Main Camera');
cameraEntity.addComponent(new TransformComponent());
const camera = new CameraComponent();
camera.orthographicSize = 1;
cameraEntity.addComponent(camera);
return cameraEntity;
});
}
}