refactor: reorganize package structure and decouple framework packages (#338)

* refactor: reorganize package structure and decouple framework packages

## Package Structure Reorganization
- Reorganized 55 packages into categorized subdirectories:
  - packages/framework/ - Generic framework (Laya/Cocos compatible)
  - packages/engine/ - ESEngine core modules
  - packages/rendering/ - Rendering modules (WASM dependent)
  - packages/physics/ - Physics modules
  - packages/streaming/ - World streaming
  - packages/network-ext/ - Network extensions
  - packages/editor/ - Editor framework and plugins
  - packages/rust/ - Rust WASM engine
  - packages/tools/ - Build tools and SDK

## Framework Package Decoupling
- Decoupled behavior-tree and blueprint packages from ESEngine dependencies
- Created abstracted interfaces (IBTAssetManager, IBehaviorTreeAssetContent)
- ESEngine-specific code moved to esengine/ subpath exports
- Framework packages now usable with Cocos/Laya without ESEngine

## CI Configuration
- Updated CI to only type-check and lint framework packages
- Added type-check:framework and lint:framework scripts

## Breaking Changes
- Package import paths changed due to directory reorganization
- ESEngine integrations now use subpath imports (e.g., '@esengine/behavior-tree/esengine')

* fix: update es-engine file path after directory reorganization

* docs: update README to focus on framework over engine

* ci: only build framework packages, remove Rust/WASM dependencies

* fix: remove esengine subpath from behavior-tree and blueprint builds

ESEngine integration code will only be available in full engine builds.
Framework packages are now purely engine-agnostic.

* fix: move network-protocols to framework, build both in CI

* fix: update workflow paths from packages/core to packages/framework/core

* fix: exclude esengine folder from type-check in behavior-tree and blueprint

* fix: update network tsconfig references to new paths

* fix: add test:ci:framework to only test framework packages in CI

* fix: only build core and math npm packages in CI

* fix: exclude test files from CodeQL and fix string escaping security issue
This commit is contained in:
YHH
2025-12-26 14:50:35 +08:00
committed by GitHub
parent a84ff902e4
commit 155411e743
1936 changed files with 4147 additions and 11578 deletions

View File

@@ -0,0 +1,215 @@
/**
* 粒子编辑器状态管理
* Particle editor state management
*/
import { create } from 'zustand';
import { subscribeWithSelector } from 'zustand/middleware';
import type { IParticleAsset } from '@esengine/particle';
import { createDefaultParticleAsset } from '@esengine/particle';
/** Tab 类型 | Tab type */
export type ParticleEditorTab = 'basic' | 'emission' | 'particle' | 'color' | 'modules' | 'burst';
/**
* 粒子编辑器状态
* Particle editor state
*/
export interface ParticleEditorState {
// ===== 文件状态 | File State =====
/** 当前编辑的文件路径 | Current file path being edited */
filePath: string | null;
/** 待打开的文件路径 | Pending file path to open */
pendingFilePath: string | null;
/** 当前粒子数据 | Current particle data */
particleData: IParticleAsset | null;
/** 是否已修改 | Is modified */
isDirty: boolean;
/** 是否正在加载 | Is loading */
isLoading: boolean;
// ===== UI 状态 | UI State =====
/** 当前激活的 Tab | Current active tab */
activeTab: ParticleEditorTab;
/** 是否全屏 | Is fullscreen */
isFullscreen: boolean;
/** 选中的预设名称 | Selected preset name */
selectedPreset: string | null;
// ===== 预览状态 | Preview State =====
/** 是否正在预览 | Is previewing */
isPlaying: boolean;
/** 是否跟随鼠标 | Is following mouse */
followMouse: boolean;
/** 爆发触发计数器 | Burst trigger counter */
burstTrigger: number;
// ===== Actions =====
/** 设置文件路径 | Set file path */
setFilePath: (path: string | null) => void;
/** 设置待打开的文件路径 | Set pending file path */
setPendingFilePath: (path: string | null) => void;
/** 设置粒子数据 | Set particle data */
setParticleData: (data: IParticleAsset | null) => void;
/** 更新粒子属性 | Update particle property */
updateProperty: <K extends keyof IParticleAsset>(key: K, value: IParticleAsset[K]) => void;
/** 标记为已修改 | Mark as dirty */
markDirty: () => void;
/** 标记为已保存 | Mark as saved */
markSaved: () => void;
/** 设置播放状态 | Set playing state */
setPlaying: (playing: boolean) => void;
/** 设置选中预设 | Set selected preset */
setSelectedPreset: (preset: string | null) => void;
/** 重置编辑器 | Reset editor */
reset: () => void;
/** 创建新粒子效果 | Create new particle effect */
createNew: (name?: string) => void;
// ===== UI Actions =====
/** 设置激活的 Tab | Set active tab */
setActiveTab: (tab: ParticleEditorTab) => void;
/** 切换全屏 | Toggle fullscreen */
toggleFullscreen: () => void;
/** 切换跟随鼠标 | Toggle follow mouse */
toggleFollowMouse: () => void;
/** 触发爆发 | Trigger burst */
triggerBurst: () => void;
// ===== 文件操作 | File Operations =====
/** 加载文件 | Load file */
loadFile: (path: string, fileSystem: { readFile: (path: string) => Promise<string> }) => Promise<void>;
/** 保存文件 | Save file */
saveFile: (fileSystem: { writeFile: (path: string, content: string) => Promise<void> }, dialogService?: { saveDialog: (options: any) => Promise<string | null> }) => Promise<boolean>;
}
/**
* 粒子编辑器 Store
* Particle editor store
*/
export const useParticleEditorStore = create<ParticleEditorState>()(
subscribeWithSelector((set, get) => ({
// ===== 初始状态 | Initial State =====
filePath: null,
pendingFilePath: null,
particleData: null,
isDirty: false,
isLoading: false,
activeTab: 'basic' as ParticleEditorTab,
isFullscreen: false,
selectedPreset: null,
isPlaying: false,
followMouse: false,
burstTrigger: 0,
// ===== 基础 Actions | Basic Actions =====
setFilePath: (path) => set({ filePath: path }),
setPendingFilePath: (path) => set({ pendingFilePath: path }),
setParticleData: (data) => set((state) => ({
particleData: data,
// 加载时不标记 dirty | Don't mark dirty when loading
isDirty: state.isLoading ? false : (state.filePath !== null || state.particleData !== null),
})),
updateProperty: (key, value) => set((state) => {
if (!state.particleData) return state;
return {
particleData: {
...state.particleData,
[key]: value,
},
isDirty: true,
};
}),
markDirty: () => set({ isDirty: true }),
markSaved: () => set({ isDirty: false }),
setPlaying: (playing) => set({ isPlaying: playing }),
setSelectedPreset: (preset) => set({ selectedPreset: preset }),
reset: () => set({
filePath: null,
pendingFilePath: null,
particleData: null,
isDirty: false,
isLoading: false,
activeTab: 'basic',
isFullscreen: false,
selectedPreset: null,
isPlaying: false,
followMouse: false,
burstTrigger: 0,
}),
createNew: (name = 'New Particle') => set({
particleData: createDefaultParticleAsset(name),
filePath: null,
isDirty: true,
isPlaying: true, // 自动播放 | Auto play
selectedPreset: null,
isLoading: false,
}),
// ===== UI Actions =====
setActiveTab: (tab) => set({ activeTab: tab }),
toggleFullscreen: () => set((state) => ({ isFullscreen: !state.isFullscreen })),
toggleFollowMouse: () => set((state) => ({ followMouse: !state.followMouse })),
triggerBurst: () => set((state) => ({ burstTrigger: state.burstTrigger + 1 })),
// ===== 文件操作 | File Operations =====
loadFile: async (path, fileSystem) => {
set({ isLoading: true });
try {
const content = await fileSystem.readFile(path);
const data = JSON.parse(content) as IParticleAsset;
const defaults = createDefaultParticleAsset();
set({
particleData: { ...defaults, ...data },
filePath: path,
isDirty: false,
isLoading: false,
pendingFilePath: null,
});
} catch (error) {
console.error('[ParticleEditorStore] Failed to load file:', error);
set({ isLoading: false });
}
},
saveFile: async (fileSystem, dialogService) => {
const state = get();
if (!state.particleData) return false;
let savePath = state.filePath;
// 如果没有路径,弹出保存对话框 | If no path, show save dialog
if (!savePath && dialogService) {
savePath = await dialogService.saveDialog({
title: 'Save Particle Effect',
filters: [{ name: 'Particle Effect', extensions: ['particle'] }],
defaultPath: `${state.particleData.name || 'new-particle'}.particle`,
});
if (!savePath) return false;
}
if (!savePath) return false;
try {
await fileSystem.writeFile(savePath, JSON.stringify(state.particleData, null, 2));
set({ filePath: savePath, isDirty: false });
return true;
} catch (error) {
console.error('[ParticleEditorStore] Failed to save:', error);
return false;
}
},
}))
);