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,294 @@
/**
* @zh 玩家会话
* @en Player Session
*
* @zh 封装单个玩家的 VM 实例、蓝图和 Memory 状态
* @en Encapsulates a single player's VM instance, blueprint and Memory state
*/
import type { BlueprintAsset } from '@esengine/blueprint';
import type { IIntent, IntentKeyExtractor } from '../intent/IntentTypes';
import type { IGameState } from '../vm/ServerExecutionContext';
import type { CPULimiterConfig } from '../vm/CPULimiter';
import { ServerBlueprintVM } from '../vm/ServerBlueprintVM';
import type { PlayerTickResult } from './types';
/**
* @zh 玩家会话配置
* @en Player session configuration
*
* @typeParam TIntent - @zh 意图类型 @en Intent type
*/
export interface PlayerSessionConfig<TIntent extends IIntent = IIntent> {
/**
* @zh CPU 限制配置
* @en CPU limit configuration
*/
readonly cpuConfig?: Partial<CPULimiterConfig>;
/**
* @zh 意图键提取器
* @en Intent key extractor
*/
readonly intentKeyExtractor?: IntentKeyExtractor<TIntent>;
/**
* @zh 调试模式
* @en Debug mode
*/
readonly debug?: boolean;
}
/**
* @zh 玩家会话状态
* @en Player session state
*/
export type PlayerSessionState = 'active' | 'suspended' | 'error';
/**
* @zh 玩家会话
* @en Player Session
*
* @zh 管理单个玩家的蓝图执行环境
* @en Manages a single player's blueprint execution environment
*
* @typeParam TGameState - @zh 游戏状态类型 @en Game state type
* @typeParam TIntent - @zh 意图类型 @en Intent type
*
* @example
* ```typescript
* const session = new PlayerSession<MyGameState, MyIntent>(
* 'player1',
* playerBlueprint,
* { cpuConfig: { maxCpuTime: 50 } }
* );
*
* const result = session.executeTick(gameState);
* ```
*/
export class PlayerSession<
TGameState extends IGameState = IGameState,
TIntent extends IIntent = IIntent
> {
private readonly _playerId: string;
private readonly _vm: ServerBlueprintVM<TGameState, TIntent>;
private _memory: Record<string, unknown>;
private _state: PlayerSessionState = 'active';
private _lastError: string | null = null;
private _totalCpuUsed: number = 0;
private _ticksExecuted: number = 0;
/**
* @param playerId - @zh 玩家 ID @en Player ID
* @param blueprint - @zh 蓝图资产 @en Blueprint asset
* @param config - @zh 配置选项 @en Configuration options
*/
constructor(
playerId: string,
blueprint: BlueprintAsset,
config: PlayerSessionConfig<TIntent> = {}
) {
this._playerId = playerId;
this._memory = {};
this._vm = new ServerBlueprintVM<TGameState, TIntent>(playerId, blueprint, {
cpuConfig: config.cpuConfig,
intentKeyExtractor: config.intentKeyExtractor,
debug: config.debug
});
}
// =========================================================================
// 属性 | Properties
// =========================================================================
/**
* @zh 获取玩家 ID
* @en Get player ID
*/
get playerId(): string {
return this._playerId;
}
/**
* @zh 获取会话状态
* @en Get session state
*/
get state(): PlayerSessionState {
return this._state;
}
/**
* @zh 获取最后一次错误
* @en Get last error
*/
get lastError(): string | null {
return this._lastError;
}
/**
* @zh 获取累计 CPU 使用时间
* @en Get total CPU time used
*/
get totalCpuUsed(): number {
return this._totalCpuUsed;
}
/**
* @zh 获取已执行的 tick 数
* @en Get number of ticks executed
*/
get ticksExecuted(): number {
return this._ticksExecuted;
}
/**
* @zh 获取当前 Memory
* @en Get current Memory
*/
get memory(): Readonly<Record<string, unknown>> {
return this._memory;
}
/**
* @zh 获取底层 VM 实例
* @en Get underlying VM instance
*/
get vm(): ServerBlueprintVM<TGameState, TIntent> {
return this._vm;
}
// =========================================================================
// Memory 管理 | Memory Management
// =========================================================================
/**
* @zh 设置 Memory
* @en Set Memory
*/
setMemory(memory: Record<string, unknown>): void {
this._memory = { ...memory };
}
/**
* @zh 更新 Memory合并
* @en Update Memory (merge)
*/
updateMemory(updates: Record<string, unknown>): void {
this._memory = { ...this._memory, ...updates };
}
/**
* @zh 清空 Memory
* @en Clear Memory
*/
clearMemory(): void {
this._memory = {};
}
// =========================================================================
// 执行 | Execution
// =========================================================================
/**
* @zh 执行一个 tick
* @en Execute one tick
*
* @param gameState - @zh 当前游戏状态 @en Current game state
* @returns @zh 执行结果 @en Execution result
*/
executeTick(gameState: TGameState): PlayerTickResult<TIntent> {
if (this._state === 'suspended') {
return this._createSkippedResult('Session is suspended');
}
try {
const result = this._vm.executeTick(gameState, this._memory);
this._memory = result.memory;
this._totalCpuUsed += result.cpu.used;
this._ticksExecuted++;
if (!result.success && result.errors.length > 0) {
this._lastError = result.errors[0];
}
return {
playerId: this._playerId,
success: result.success,
cpu: result.cpu,
intents: result.intents,
logs: result.logs,
errors: result.errors,
memory: result.memory
};
} catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
this._state = 'error';
this._lastError = errorMessage;
return this._createSkippedResult(errorMessage);
}
}
// =========================================================================
// 状态管理 | State Management
// =========================================================================
/**
* @zh 暂停会话
* @en Suspend session
*/
suspend(): void {
this._state = 'suspended';
}
/**
* @zh 恢复会话
* @en Resume session
*/
resume(): void {
if (this._state === 'suspended') {
this._state = 'active';
}
}
/**
* @zh 重置会话
* @en Reset session
*/
reset(): void {
this._vm.reset();
this._state = 'active';
this._lastError = null;
this._totalCpuUsed = 0;
this._ticksExecuted = 0;
}
/**
* @zh 从错误状态恢复
* @en Recover from error state
*/
recover(): void {
if (this._state === 'error') {
this._vm.reset();
this._state = 'active';
this._lastError = null;
}
}
// =========================================================================
// 私有方法 | Private Methods
// =========================================================================
private _createSkippedResult(error: string): PlayerTickResult<TIntent> {
return {
playerId: this._playerId,
success: false,
cpu: { used: 0, limit: 0, bucket: 0, steps: 0, maxSteps: 0, exceeded: false },
intents: [],
logs: [],
errors: [error],
memory: this._memory
};
}
}