* 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
295 lines
7.8 KiB
TypeScript
295 lines
7.8 KiB
TypeScript
/**
|
||
* @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
|
||
};
|
||
}
|
||
}
|