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,201 @@
/**
* @zh 游戏信息节点
* @en Game Info Nodes
*
* @zh 提供获取游戏状态信息的能力
* @en Provides access to game state information
*/
import type { BlueprintNodeTemplate, BlueprintNode } from '@esengine/blueprint';
import type { INodeExecutor, ExecutionResult } from '@esengine/blueprint';
import type { IGameState } from '../vm/ServerExecutionContext';
// =============================================================================
// 扩展的执行上下文接口 | Extended Execution Context Interface
// =============================================================================
interface ServerContext {
gameState: IGameState | null;
playerId: string;
deltaTime: number;
}
// =============================================================================
// GetTick Node | 获取 Tick 节点
// =============================================================================
/**
* @zh 获取 Tick 节点模板
* @en Get Tick node template
*/
export const GetTickTemplate: BlueprintNodeTemplate = {
type: 'GetTick',
title: 'Get Tick',
category: 'time',
description: 'Get the current game tick / 获取当前游戏 tick',
keywords: ['tick', 'time', 'frame', 'turn'],
menuPath: ['Game', 'Get Tick'],
isPure: true,
inputs: [],
outputs: [
{
name: 'tick',
displayName: 'Tick',
type: 'int'
}
],
color: '#1e6b8b'
};
/**
* @zh 获取 Tick 节点执行器
* @en Get Tick node executor
*/
export class GetTickExecutor implements INodeExecutor {
execute(_node: BlueprintNode, context: unknown): ExecutionResult {
const ctx = context as ServerContext;
const tick = ctx.gameState?.tick ?? 0;
return {
outputs: { tick }
};
}
}
// =============================================================================
// GetPlayerId Node | 获取玩家 ID 节点
// =============================================================================
/**
* @zh 获取玩家 ID 节点模板
* @en Get Player ID node template
*/
export const GetPlayerIdTemplate: BlueprintNodeTemplate = {
type: 'GetPlayerId',
title: 'Get Player ID',
category: 'entity',
description: 'Get the current player ID / 获取当前玩家 ID',
keywords: ['player', 'id', 'owner', 'me'],
menuPath: ['Game', 'Get Player ID'],
isPure: true,
inputs: [],
outputs: [
{
name: 'playerId',
displayName: 'Player ID',
type: 'string'
}
],
color: '#1e5a8b'
};
/**
* @zh 获取玩家 ID 节点执行器
* @en Get Player ID node executor
*/
export class GetPlayerIdExecutor implements INodeExecutor {
execute(_node: BlueprintNode, context: unknown): ExecutionResult {
const ctx = context as ServerContext;
return {
outputs: { playerId: ctx.playerId }
};
}
}
// =============================================================================
// GetDeltaTime Node | 获取增量时间节点
// =============================================================================
/**
* @zh 获取增量时间节点模板
* @en Get Delta Time node template
*/
export const GetDeltaTimeTemplate: BlueprintNodeTemplate = {
type: 'GetDeltaTime',
title: 'Get Delta Time',
category: 'time',
description: 'Get the time since last tick (seconds) / 获取距上次 tick 的时间(秒)',
keywords: ['delta', 'time', 'dt', 'interval'],
menuPath: ['Game', 'Get Delta Time'],
isPure: true,
inputs: [],
outputs: [
{
name: 'deltaTime',
displayName: 'Delta Time',
type: 'float'
}
],
color: '#1e6b8b'
};
/**
* @zh 获取增量时间节点执行器
* @en Get Delta Time node executor
*/
export class GetDeltaTimeExecutor implements INodeExecutor {
execute(_node: BlueprintNode, context: unknown): ExecutionResult {
const ctx = context as ServerContext;
return {
outputs: { deltaTime: ctx.deltaTime }
};
}
}
// =============================================================================
// GetGameState Node | 获取游戏状态节点
// =============================================================================
/**
* @zh 获取游戏状态节点模板
* @en Get Game State node template
*/
export const GetGameStateTemplate: BlueprintNodeTemplate = {
type: 'GetGameState',
title: 'Get Game State',
category: 'entity',
description: 'Get the current game state object / 获取当前游戏状态对象',
keywords: ['game', 'state', 'world', 'data'],
menuPath: ['Game', 'Get Game State'],
isPure: true,
inputs: [],
outputs: [
{
name: 'state',
displayName: 'State',
type: 'object'
}
],
color: '#1e5a8b'
};
/**
* @zh 获取游戏状态节点执行器
* @en Get Game State node executor
*/
export class GetGameStateExecutor implements INodeExecutor {
execute(_node: BlueprintNode, context: unknown): ExecutionResult {
const ctx = context as ServerContext;
return {
outputs: { state: ctx.gameState }
};
}
}
// =============================================================================
// 节点定义集合 | Node Definition Collection
// =============================================================================
/**
* @zh 游戏信息节点定义
* @en Game info node definitions
*/
export const GameInfoNodeDefinitions = [
{ template: GetTickTemplate, executor: new GetTickExecutor() },
{ template: GetPlayerIdTemplate, executor: new GetPlayerIdExecutor() },
{ template: GetDeltaTimeTemplate, executor: new GetDeltaTimeExecutor() },
{ template: GetGameStateTemplate, executor: new GetGameStateExecutor() }
];

View File

@@ -0,0 +1,200 @@
/**
* @zh 日志节点
* @en Log Nodes
*
* @zh 提供日志记录能力
* @en Provides logging capabilities
*/
import type { BlueprintNodeTemplate, BlueprintNode } from '@esengine/blueprint';
import type { INodeExecutor, ExecutionResult } from '@esengine/blueprint';
// =============================================================================
// 扩展的执行上下文接口 | Extended Execution Context Interface
// =============================================================================
interface ServerContext {
evaluateInput(nodeId: string, pinName: string, defaultValue?: unknown): unknown;
log: (message: string) => void;
warn: (message: string) => void;
error: (message: string) => void;
}
// =============================================================================
// Log Node | 日志节点
// =============================================================================
/**
* @zh 日志节点模板
* @en Log node template
*/
export const LogTemplate: BlueprintNodeTemplate = {
type: 'Log',
title: 'Log',
category: 'debug',
description: 'Log a message / 记录日志消息',
keywords: ['log', 'print', 'debug', 'console'],
menuPath: ['Debug', 'Log'],
inputs: [
{
name: 'exec',
displayName: '',
type: 'exec'
},
{
name: 'message',
displayName: 'Message',
type: 'string',
defaultValue: ''
}
],
outputs: [
{
name: 'exec',
displayName: '',
type: 'exec'
}
],
color: '#5a5a5a'
};
/**
* @zh 日志节点执行器
* @en Log node executor
*/
export class LogExecutor implements INodeExecutor {
execute(node: BlueprintNode, context: unknown): ExecutionResult {
const ctx = context as ServerContext;
const message = ctx.evaluateInput(node.id, 'message', '') as string;
ctx.log(String(message));
return {
nextExec: 'exec'
};
}
}
// =============================================================================
// Warn Node | 警告节点
// =============================================================================
/**
* @zh 警告节点模板
* @en Warn node template
*/
export const WarnTemplate: BlueprintNodeTemplate = {
type: 'Warn',
title: 'Warn',
category: 'debug',
description: 'Log a warning message / 记录警告消息',
keywords: ['warn', 'warning', 'debug', 'console'],
menuPath: ['Debug', 'Warn'],
inputs: [
{
name: 'exec',
displayName: '',
type: 'exec'
},
{
name: 'message',
displayName: 'Message',
type: 'string',
defaultValue: ''
}
],
outputs: [
{
name: 'exec',
displayName: '',
type: 'exec'
}
],
color: '#8b8b1e'
};
/**
* @zh 警告节点执行器
* @en Warn node executor
*/
export class WarnExecutor implements INodeExecutor {
execute(node: BlueprintNode, context: unknown): ExecutionResult {
const ctx = context as ServerContext;
const message = ctx.evaluateInput(node.id, 'message', '') as string;
ctx.warn(String(message));
return {
nextExec: 'exec'
};
}
}
// =============================================================================
// Error Node | 错误节点
// =============================================================================
/**
* @zh 错误节点模板
* @en Error node template
*/
export const ErrorTemplate: BlueprintNodeTemplate = {
type: 'Error',
title: 'Error',
category: 'debug',
description: 'Log an error message / 记录错误消息',
keywords: ['error', 'debug', 'console'],
menuPath: ['Debug', 'Error'],
inputs: [
{
name: 'exec',
displayName: '',
type: 'exec'
},
{
name: 'message',
displayName: 'Message',
type: 'string',
defaultValue: ''
}
],
outputs: [
{
name: 'exec',
displayName: '',
type: 'exec'
}
],
color: '#8b1e1e'
};
/**
* @zh 错误节点执行器
* @en Error node executor
*/
export class ErrorExecutor implements INodeExecutor {
execute(node: BlueprintNode, context: unknown): ExecutionResult {
const ctx = context as ServerContext;
const message = ctx.evaluateInput(node.id, 'message', '') as string;
ctx.error(String(message));
return {
nextExec: 'exec'
};
}
}
// =============================================================================
// 节点定义集合 | Node Definition Collection
// =============================================================================
/**
* @zh 日志节点定义
* @en Log node definitions
*/
export const LogNodeDefinitions = [
{ template: LogTemplate, executor: new LogExecutor() },
{ template: WarnTemplate, executor: new WarnExecutor() },
{ template: ErrorTemplate, executor: new ErrorExecutor() }
];

View File

@@ -0,0 +1,269 @@
/**
* @zh Memory 操作节点
* @en Memory Operation Nodes
*
* @zh 提供玩家持久化数据的读写能力
* @en Provides read/write access to player persistent data
*/
import type { BlueprintNodeTemplate, BlueprintNode } from '@esengine/blueprint';
import type { INodeExecutor, ExecutionResult } from '@esengine/blueprint';
// =============================================================================
// 扩展的执行上下文接口 | Extended Execution Context Interface
// =============================================================================
/**
* @zh 服务器端执行上下文接口(用于节点)
* @en Server-side execution context interface (for nodes)
*/
interface ServerContext {
memory: Record<string, unknown>;
evaluateInput(nodeId: string, pinName: string, defaultValue?: unknown): unknown;
setOutputs(nodeId: string, outputs: Record<string, unknown>): void;
}
// =============================================================================
// GetMemory Node | 获取 Memory 节点
// =============================================================================
/**
* @zh 获取 Memory 节点模板
* @en Get Memory node template
*/
export const GetMemoryTemplate: BlueprintNodeTemplate = {
type: 'GetMemory',
title: 'Get Memory',
category: 'variable',
description: 'Get a value from player Memory / 从玩家 Memory 获取值',
keywords: ['memory', 'get', 'read', 'load', 'data'],
menuPath: ['Memory', 'Get Memory'],
isPure: true,
inputs: [
{
name: 'key',
displayName: 'Key',
type: 'string',
defaultValue: ''
},
{
name: 'defaultValue',
displayName: 'Default',
type: 'any',
defaultValue: null
}
],
outputs: [
{
name: 'value',
displayName: 'Value',
type: 'any'
}
],
color: '#8b5a8b'
};
/**
* @zh 获取 Memory 节点执行器
* @en Get Memory node executor
*/
export class GetMemoryExecutor implements INodeExecutor {
execute(node: BlueprintNode, context: unknown): ExecutionResult {
const ctx = context as ServerContext;
const key = ctx.evaluateInput(node.id, 'key', '') as string;
const defaultValue = ctx.evaluateInput(node.id, 'defaultValue', null);
const value = ctx.memory[key] ?? defaultValue;
return {
outputs: { value }
};
}
}
// =============================================================================
// SetMemory Node | 设置 Memory 节点
// =============================================================================
/**
* @zh 设置 Memory 节点模板
* @en Set Memory node template
*/
export const SetMemoryTemplate: BlueprintNodeTemplate = {
type: 'SetMemory',
title: 'Set Memory',
category: 'variable',
description: 'Set a value in player Memory / 在玩家 Memory 中设置值',
keywords: ['memory', 'set', 'write', 'save', 'data'],
menuPath: ['Memory', 'Set Memory'],
inputs: [
{
name: 'exec',
displayName: '',
type: 'exec'
},
{
name: 'key',
displayName: 'Key',
type: 'string',
defaultValue: ''
},
{
name: 'value',
displayName: 'Value',
type: 'any',
defaultValue: null
}
],
outputs: [
{
name: 'exec',
displayName: '',
type: 'exec'
}
],
color: '#8b5a8b'
};
/**
* @zh 设置 Memory 节点执行器
* @en Set Memory node executor
*/
export class SetMemoryExecutor implements INodeExecutor {
execute(node: BlueprintNode, context: unknown): ExecutionResult {
const ctx = context as ServerContext;
const key = ctx.evaluateInput(node.id, 'key', '') as string;
const value = ctx.evaluateInput(node.id, 'value', null);
if (key) {
ctx.memory[key] = value;
}
return {
nextExec: 'exec'
};
}
}
// =============================================================================
// HasMemoryKey Node | 检查 Memory 键节点
// =============================================================================
/**
* @zh 检查 Memory 键节点模板
* @en Has Memory Key node template
*/
export const HasMemoryKeyTemplate: BlueprintNodeTemplate = {
type: 'HasMemoryKey',
title: 'Has Memory Key',
category: 'variable',
description: 'Check if a key exists in Memory / 检查 Memory 中是否存在某个键',
keywords: ['memory', 'has', 'exists', 'check', 'key'],
menuPath: ['Memory', 'Has Key'],
isPure: true,
inputs: [
{
name: 'key',
displayName: 'Key',
type: 'string',
defaultValue: ''
}
],
outputs: [
{
name: 'exists',
displayName: 'Exists',
type: 'bool'
}
],
color: '#8b5a8b'
};
/**
* @zh 检查 Memory 键节点执行器
* @en Has Memory Key node executor
*/
export class HasMemoryKeyExecutor implements INodeExecutor {
execute(node: BlueprintNode, context: unknown): ExecutionResult {
const ctx = context as ServerContext;
const key = ctx.evaluateInput(node.id, 'key', '') as string;
const exists = key in ctx.memory;
return {
outputs: { exists }
};
}
}
// =============================================================================
// DeleteMemory Node | 删除 Memory 节点
// =============================================================================
/**
* @zh 删除 Memory 节点模板
* @en Delete Memory node template
*/
export const DeleteMemoryTemplate: BlueprintNodeTemplate = {
type: 'DeleteMemory',
title: 'Delete Memory',
category: 'variable',
description: 'Delete a key from Memory / 从 Memory 中删除键',
keywords: ['memory', 'delete', 'remove', 'clear', 'key'],
menuPath: ['Memory', 'Delete'],
inputs: [
{
name: 'exec',
displayName: '',
type: 'exec'
},
{
name: 'key',
displayName: 'Key',
type: 'string',
defaultValue: ''
}
],
outputs: [
{
name: 'exec',
displayName: '',
type: 'exec'
}
],
color: '#8b5a8b'
};
/**
* @zh 删除 Memory 节点执行器
* @en Delete Memory node executor
*/
export class DeleteMemoryExecutor implements INodeExecutor {
execute(node: BlueprintNode, context: unknown): ExecutionResult {
const ctx = context as ServerContext;
const key = ctx.evaluateInput(node.id, 'key', '') as string;
if (key) {
delete ctx.memory[key];
}
return {
nextExec: 'exec'
};
}
}
// =============================================================================
// 节点定义集合 | Node Definition Collection
// =============================================================================
/**
* @zh Memory 节点定义
* @en Memory node definitions
*/
export const MemoryNodeDefinitions = [
{ template: GetMemoryTemplate, executor: new GetMemoryExecutor() },
{ template: SetMemoryTemplate, executor: new SetMemoryExecutor() },
{ template: HasMemoryKeyTemplate, executor: new HasMemoryKeyExecutor() },
{ template: DeleteMemoryTemplate, executor: new DeleteMemoryExecutor() }
];

View File

@@ -0,0 +1,84 @@
/**
* @zh 蓝图节点模块
* @en Blueprint Nodes Module
*
* @zh 提供服务器端蓝图执行所需的通用节点
* @en Provides common nodes for server-side blueprint execution
*/
import { NodeRegistry } from '@esengine/blueprint';
// Memory Nodes
export {
GetMemoryTemplate,
GetMemoryExecutor,
SetMemoryTemplate,
SetMemoryExecutor,
HasMemoryKeyTemplate,
HasMemoryKeyExecutor,
DeleteMemoryTemplate,
DeleteMemoryExecutor,
MemoryNodeDefinitions
} from './MemoryNodes';
// Log Nodes
export {
LogTemplate,
LogExecutor,
WarnTemplate,
WarnExecutor,
ErrorTemplate,
ErrorExecutor,
LogNodeDefinitions
} from './LogNodes';
// Game Info Nodes
export {
GetTickTemplate,
GetTickExecutor,
GetPlayerIdTemplate,
GetPlayerIdExecutor,
GetDeltaTimeTemplate,
GetDeltaTimeExecutor,
GetGameStateTemplate,
GetGameStateExecutor,
GameInfoNodeDefinitions
} from './GameInfoNodes';
// =============================================================================
// 节点注册 | Node Registration
// =============================================================================
import { MemoryNodeDefinitions } from './MemoryNodes';
import { LogNodeDefinitions } from './LogNodes';
import { GameInfoNodeDefinitions } from './GameInfoNodes';
/**
* @zh 所有节点定义
* @en All node definitions
*/
export const AllNodeDefinitions = [
...MemoryNodeDefinitions,
...LogNodeDefinitions,
...GameInfoNodeDefinitions
];
/**
* @zh 注册所有 script-runtime 节点到 NodeRegistry
* @en Register all script-runtime nodes to NodeRegistry
*
* @example
* ```typescript
* import { registerScriptRuntimeNodes } from '@esengine/script-runtime';
*
* // 在应用启动时调用 | Call at application startup
* registerScriptRuntimeNodes();
* ```
*/
export function registerScriptRuntimeNodes(): void {
const registry = NodeRegistry.instance;
for (const { template, executor } of AllNodeDefinitions) {
registry.register(template, executor);
}
}