Files
esengine/packages/engine/script-runtime/src/vm/ServerExecutionContext.ts
YHH 155411e743 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
2025-12-26 14:50:35 +08:00

460 lines
12 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* @zh 服务器端执行上下文
* @en Server-side Execution Context
*
* @zh 扩展 Blueprint 的 ExecutionContext添加服务器端特性
* @en Extends Blueprint's ExecutionContext with server-side features
*/
import type { BlueprintAsset } from '@esengine/blueprint';
import type { IIntent } from '../intent/IntentTypes';
import type { IIntentCollector } from '../intent/IntentCollector';
import type { CPULimiter } from './CPULimiter';
// =============================================================================
// 基础游戏状态接口 | Base Game State Interface
// =============================================================================
/**
* @zh 基础游戏状态接口(引擎级别)
* @en Base game state interface (engine level)
*
* @zh 引擎只定义最基础的时间信息,具体的游戏状态由游戏项目扩展
* @en Engine only defines basic timing info, specific game state is extended by game projects
*
* @example
* ```typescript
* // 游戏项目中扩展游戏状态 | Extend game state in game project
* interface MyGameState extends IGameState {
* units: Map<string, IUnitState>;
* buildings: Map<string, IBuildingState>;
* }
* ```
*/
export interface IGameState {
/**
* @zh 当前 tick
* @en Current tick
*/
tick: number;
/**
* @zh 自上次 tick 的时间间隔
* @en Time interval since last tick
*/
deltaTime: number;
}
// =============================================================================
// 日志接口 | Log Interface
// =============================================================================
/**
* @zh 日志条目
* @en Log entry
*/
export interface LogEntry {
level: 'log' | 'warn' | 'error';
message: string;
timestamp: number;
tick: number;
}
// =============================================================================
// 服务器执行上下文 | Server Execution Context
// =============================================================================
/**
* @zh 服务器端执行上下文
* @en Server-side Execution Context
*
* @zh 提供蓝图执行时访问游戏状态和收集意图的能力
* @en Provides ability to access game state and collect intents during blueprint execution
*
* @typeParam TGameState - @zh 游戏状态类型,由游戏项目定义 @en Game state type, defined by game project
* @typeParam TIntent - @zh 意图类型,由游戏项目定义 @en Intent type, defined by game project
*
* @example
* ```typescript
* // 游戏项目中定义具体类型 | Define specific types in game project
* interface MyGameState extends IGameState {
* units: Map<string, IUnit>;
* resources: Map<string, IResource>;
* }
*
* interface MyIntent extends IIntent {
* readonly type: 'unit.move' | 'unit.attack';
* unitId: string;
* }
*
* // 创建上下文 | Create context
* const context = new ServerExecutionContext<MyGameState, MyIntent>(blueprint, 'player1');
* ```
*/
export class ServerExecutionContext<
TGameState extends IGameState = IGameState,
TIntent extends IIntent = IIntent
> {
/**
* @zh 蓝图资产
* @en Blueprint asset
*/
readonly blueprint: BlueprintAsset;
/**
* @zh 玩家 ID
* @en Player ID
*/
readonly playerId: string;
/**
* @zh 当前游戏状态
* @en Current game state
*/
private _gameState: TGameState | null = null;
/**
* @zh 意图收集器
* @en Intent collector
*/
private _intentCollector: IIntentCollector<TIntent> | null = null;
/**
* @zh CPU 限制器
* @en CPU limiter
*/
private _cpuLimiter: CPULimiter | null = null;
/**
* @zh 玩家持久化数据Memory
* @en Player persistent data (Memory)
*/
private _memory: Record<string, unknown> = {};
/**
* @zh 日志列表
* @en Log list
*/
private _logs: LogEntry[] = [];
/**
* @zh 帧增量时间
* @en Frame delta time
*/
deltaTime: number = 0;
/**
* @zh 自开始以来的总时间
* @en Total time since start
*/
time: number = 0;
/**
* @zh 节点输出缓存
* @en Node output cache
*/
private _outputCache: Map<string, Record<string, unknown>> = new Map();
/**
* @zh 实例变量
* @en Instance variables
*/
private _instanceVariables: Map<string, unknown> = new Map();
/**
* @zh 局部变量
* @en Local variables
*/
private _localVariables: Map<string, unknown> = new Map();
/**
* @zh 全局变量(所有玩家共享)
* @en Global variables (shared by all players)
*/
private static _globalVariables: Map<string, unknown> = new Map();
constructor(blueprint: BlueprintAsset, playerId: string) {
this.blueprint = blueprint;
this.playerId = playerId;
for (const variable of blueprint.variables) {
if (variable.scope === 'instance') {
this._instanceVariables.set(variable.name, variable.defaultValue);
}
}
}
// =========================================================================
// 游戏状态访问 | Game State Access
// =========================================================================
/**
* @zh 设置当前游戏状态
* @en Set current game state
*/
setGameState(state: TGameState): void {
this._gameState = state;
this.deltaTime = state.deltaTime;
}
/**
* @zh 获取当前游戏状态
* @en Get current game state
*/
getGameState(): TGameState | null {
return this._gameState;
}
/**
* @zh 获取当前 tick
* @en Get current tick
*/
getTick(): number {
return this._gameState?.tick ?? 0;
}
// =========================================================================
// 意图收集 | Intent Collection
// =========================================================================
/**
* @zh 设置意图收集器
* @en Set intent collector
*/
setIntentCollector(collector: IIntentCollector<TIntent>): void {
this._intentCollector = collector;
}
/**
* @zh 获取意图收集器
* @en Get intent collector
*/
get intentCollector(): IIntentCollector<TIntent> | null {
return this._intentCollector;
}
// =========================================================================
// CPU 限制 | CPU Limiting
// =========================================================================
/**
* @zh 设置 CPU 限制器
* @en Set CPU limiter
*/
setCPULimiter(limiter: CPULimiter): void {
this._cpuLimiter = limiter;
}
/**
* @zh 获取 CPU 限制器
* @en Get CPU limiter
*/
get cpuLimiter(): CPULimiter | null {
return this._cpuLimiter;
}
/**
* @zh 检查是否可以继续执行
* @en Check if can continue execution
*/
checkCPU(): boolean {
return this._cpuLimiter?.checkStep() ?? true;
}
// =========================================================================
// Memory 访问 | Memory Access
// =========================================================================
/**
* @zh 设置玩家 Memory
* @en Set player Memory
*/
setMemory(memory: Record<string, unknown>): void {
this._memory = memory;
}
/**
* @zh 获取玩家 Memory
* @en Get player Memory
*/
getMemory(): Record<string, unknown> {
return this._memory;
}
/**
* @zh 获取 Memory 中的值
* @en Get value from Memory
*/
getMemoryValue<T>(key: string): T | undefined {
return this._memory[key] as T | undefined;
}
/**
* @zh 设置 Memory 中的值
* @en Set value in Memory
*/
setMemoryValue(key: string, value: unknown): void {
this._memory[key] = value;
}
// =========================================================================
// 日志 | Logging
// =========================================================================
/**
* @zh 添加日志
* @en Add log
*/
log(message: string): void {
this._logs.push({
level: 'log',
message,
timestamp: Date.now(),
tick: this.getTick()
});
}
/**
* @zh 添加警告
* @en Add warning
*/
warn(message: string): void {
this._logs.push({
level: 'warn',
message,
timestamp: Date.now(),
tick: this.getTick()
});
}
/**
* @zh 添加错误
* @en Add error
*/
error(message: string): void {
this._logs.push({
level: 'error',
message,
timestamp: Date.now(),
tick: this.getTick()
});
}
/**
* @zh 获取日志列表
* @en Get log list
*/
getLogs(): LogEntry[] {
return [...this._logs];
}
/**
* @zh 清除日志
* @en Clear logs
*/
clearLogs(): void {
this._logs = [];
}
// =========================================================================
// 变量管理 | Variable Management
// =========================================================================
/**
* @zh 获取变量值
* @en Get variable value
*/
getVariable(name: string): unknown {
if (this._localVariables.has(name)) {
return this._localVariables.get(name);
}
if (this._instanceVariables.has(name)) {
return this._instanceVariables.get(name);
}
if (ServerExecutionContext._globalVariables.has(name)) {
return ServerExecutionContext._globalVariables.get(name);
}
const varDef = this.blueprint.variables.find(v => v.name === name);
return varDef?.defaultValue;
}
/**
* @zh 设置变量值
* @en Set variable value
*/
setVariable(name: string, value: unknown): void {
const varDef = this.blueprint.variables.find(v => v.name === name);
if (!varDef) {
this._localVariables.set(name, value);
return;
}
switch (varDef.scope) {
case 'local':
this._localVariables.set(name, value);
break;
case 'instance':
this._instanceVariables.set(name, value);
break;
case 'global':
ServerExecutionContext._globalVariables.set(name, value);
break;
}
}
/**
* @zh 获取实例变量
* @en Get instance variables
*/
getInstanceVariables(): Map<string, unknown> {
return new Map(this._instanceVariables);
}
/**
* @zh 设置实例变量
* @en Set instance variables
*/
setInstanceVariables(variables: Map<string, unknown>): void {
this._instanceVariables = new Map(variables);
}
// =========================================================================
// 输出缓存 | Output Cache
// =========================================================================
/**
* @zh 设置节点输出
* @en Set node outputs
*/
setOutputs(nodeId: string, outputs: Record<string, unknown>): void {
this._outputCache.set(nodeId, outputs);
}
/**
* @zh 获取节点输出
* @en Get node outputs
*/
getOutputs(nodeId: string): Record<string, unknown> | undefined {
return this._outputCache.get(nodeId);
}
/**
* @zh 清除输出缓存
* @en Clear output cache
*/
clearOutputCache(): void {
this._outputCache.clear();
this._localVariables.clear();
}
/**
* @zh 清除全局变量
* @en Clear global variables
*/
static clearGlobalVariables(): void {
ServerExecutionContext._globalVariables.clear();
}
}