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:
294
packages/engine/script-runtime/src/server/PlayerSession.ts
Normal file
294
packages/engine/script-runtime/src/server/PlayerSession.ts
Normal 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
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user