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,497 @@
/**
* @zh 蓝图触发器
* @en Blueprint Trigger
*
* @zh 定义触发器的核心实现
* @en Defines core trigger implementation
*/
import type { TriggerType, ITriggerContext } from './TriggerTypes';
import type { ITriggerCondition } from './TriggerCondition';
import { AlwaysTrueCondition } from './TriggerCondition';
// =============================================================================
// 触发器接口 | Trigger Interface
// =============================================================================
/**
* @zh 触发器回调函数类型
* @en Trigger callback function type
*/
export type TriggerCallback = (context: ITriggerContext) => void;
/**
* @zh 蓝图触发器接口
* @en Blueprint trigger interface
*/
export interface IBlueprintTrigger {
/**
* @zh 触发器唯一标识
* @en Trigger unique identifier
*/
readonly id: string;
/**
* @zh 触发器类型
* @en Trigger type
*/
readonly type: TriggerType;
/**
* @zh 触发器条件
* @en Trigger conditions
*/
readonly condition: ITriggerCondition;
/**
* @zh 是否启用
* @en Is enabled
*/
enabled: boolean;
/**
* @zh 优先级(越高越先执行)
* @en Priority (higher executes first)
*/
readonly priority: number;
/**
* @zh 检查是否应该触发
* @en Check if should fire
*/
shouldFire(context: ITriggerContext): boolean;
/**
* @zh 执行触发器
* @en Execute trigger
*/
fire(context: ITriggerContext): void;
}
/**
* @zh 触发器配置
* @en Trigger configuration
*/
export interface TriggerConfig {
/**
* @zh 触发器 ID
* @en Trigger ID
*/
id?: string;
/**
* @zh 触发器类型
* @en Trigger type
*/
type: TriggerType;
/**
* @zh 触发条件
* @en Trigger condition
*/
condition?: ITriggerCondition;
/**
* @zh 是否启用
* @en Is enabled
*/
enabled?: boolean;
/**
* @zh 优先级
* @en Priority
*/
priority?: number;
/**
* @zh 回调函数
* @en Callback function
*/
callback?: TriggerCallback;
}
// =============================================================================
// 触发器实现 | Trigger Implementation
// =============================================================================
let _triggerId = 0;
/**
* @zh 生成唯一触发器 ID
* @en Generate unique trigger ID
*/
function generateTriggerId(): string {
return `trigger_${++_triggerId}`;
}
/**
* @zh 蓝图触发器实现
* @en Blueprint trigger implementation
*/
export class BlueprintTrigger implements IBlueprintTrigger {
readonly id: string;
readonly type: TriggerType;
readonly condition: ITriggerCondition;
readonly priority: number;
enabled: boolean;
private readonly _callback?: TriggerCallback;
private readonly _callbacks: Set<TriggerCallback> = new Set();
constructor(config: TriggerConfig) {
this.id = config.id ?? generateTriggerId();
this.type = config.type;
this.condition = config.condition ?? new AlwaysTrueCondition();
this.priority = config.priority ?? 0;
this.enabled = config.enabled ?? true;
this._callback = config.callback;
}
/**
* @zh 检查是否应该触发
* @en Check if should fire
*/
shouldFire(context: ITriggerContext): boolean {
if (!this.enabled) {
return false;
}
if (context.type !== this.type && this.type !== 'custom') {
return false;
}
return this.condition.evaluate(context);
}
/**
* @zh 执行触发器
* @en Execute trigger
*/
fire(context: ITriggerContext): void {
if (this._callback) {
this._callback(context);
}
for (const callback of this._callbacks) {
callback(context);
}
}
/**
* @zh 添加回调
* @en Add callback
*/
addCallback(callback: TriggerCallback): void {
this._callbacks.add(callback);
}
/**
* @zh 移除回调
* @en Remove callback
*/
removeCallback(callback: TriggerCallback): void {
this._callbacks.delete(callback);
}
/**
* @zh 清除所有回调
* @en Clear all callbacks
*/
clearCallbacks(): void {
this._callbacks.clear();
}
}
// =============================================================================
// 触发器注册表 | Trigger Registry
// =============================================================================
/**
* @zh 触发器注册表接口
* @en Trigger registry interface
*/
export interface ITriggerRegistry {
/**
* @zh 注册触发器
* @en Register trigger
*/
register(trigger: IBlueprintTrigger): void;
/**
* @zh 注销触发器
* @en Unregister trigger
*/
unregister(triggerId: string): boolean;
/**
* @zh 获取触发器
* @en Get trigger
*/
get(triggerId: string): IBlueprintTrigger | undefined;
/**
* @zh 获取所有触发器
* @en Get all triggers
*/
getAll(): IBlueprintTrigger[];
/**
* @zh 按类型获取触发器
* @en Get triggers by type
*/
getByType(type: TriggerType): IBlueprintTrigger[];
/**
* @zh 清除所有触发器
* @en Clear all triggers
*/
clear(): void;
}
/**
* @zh 触发器注册表实现
* @en Trigger registry implementation
*/
export class TriggerRegistry implements ITriggerRegistry {
private readonly _triggers: Map<string, IBlueprintTrigger> = new Map();
private readonly _triggersByType: Map<TriggerType, Set<string>> = new Map();
/**
* @zh 注册触发器
* @en Register trigger
*/
register(trigger: IBlueprintTrigger): void {
if (this._triggers.has(trigger.id)) {
console.warn(`Trigger ${trigger.id} already registered, overwriting`);
}
this._triggers.set(trigger.id, trigger);
if (!this._triggersByType.has(trigger.type)) {
this._triggersByType.set(trigger.type, new Set());
}
this._triggersByType.get(trigger.type)!.add(trigger.id);
}
/**
* @zh 注销触发器
* @en Unregister trigger
*/
unregister(triggerId: string): boolean {
const trigger = this._triggers.get(triggerId);
if (!trigger) {
return false;
}
this._triggers.delete(triggerId);
const typeSet = this._triggersByType.get(trigger.type);
if (typeSet) {
typeSet.delete(triggerId);
}
return true;
}
/**
* @zh 获取触发器
* @en Get trigger
*/
get(triggerId: string): IBlueprintTrigger | undefined {
return this._triggers.get(triggerId);
}
/**
* @zh 获取所有触发器
* @en Get all triggers
*/
getAll(): IBlueprintTrigger[] {
return Array.from(this._triggers.values());
}
/**
* @zh 按类型获取触发器
* @en Get triggers by type
*/
getByType(type: TriggerType): IBlueprintTrigger[] {
const typeSet = this._triggersByType.get(type);
if (!typeSet) {
return [];
}
const triggers: IBlueprintTrigger[] = [];
for (const id of typeSet) {
const trigger = this._triggers.get(id);
if (trigger) {
triggers.push(trigger);
}
}
return triggers.sort((a, b) => b.priority - a.priority);
}
/**
* @zh 清除所有触发器
* @en Clear all triggers
*/
clear(): void {
this._triggers.clear();
this._triggersByType.clear();
}
/**
* @zh 获取触发器数量
* @en Get trigger count
*/
get count(): number {
return this._triggers.size;
}
}
// =============================================================================
// 工厂函数 | Factory Functions
// =============================================================================
/**
* @zh 创建触发器
* @en Create trigger
*/
export function createTrigger(config: TriggerConfig): BlueprintTrigger {
return new BlueprintTrigger(config);
}
/**
* @zh 创建 Tick 触发器
* @en Create tick trigger
*/
export function createTickTrigger(
callback?: TriggerCallback,
options?: { id?: string; condition?: ITriggerCondition; priority?: number }
): BlueprintTrigger {
return new BlueprintTrigger({
id: options?.id,
type: 'tick',
condition: options?.condition,
priority: options?.priority,
callback
});
}
/**
* @zh 创建输入触发器
* @en Create input trigger
*/
export function createInputTrigger(
callback?: TriggerCallback,
options?: { id?: string; condition?: ITriggerCondition; priority?: number }
): BlueprintTrigger {
return new BlueprintTrigger({
id: options?.id,
type: 'input',
condition: options?.condition,
priority: options?.priority,
callback
});
}
/**
* @zh 创建碰撞触发器
* @en Create collision trigger
*/
export function createCollisionTrigger(
callback?: TriggerCallback,
options?: { id?: string; condition?: ITriggerCondition; priority?: number }
): BlueprintTrigger {
return new BlueprintTrigger({
id: options?.id,
type: 'collision',
condition: options?.condition,
priority: options?.priority,
callback
});
}
/**
* @zh 创建消息触发器
* @en Create message trigger
*/
export function createMessageTrigger(
callback?: TriggerCallback,
options?: { id?: string; condition?: ITriggerCondition; priority?: number }
): BlueprintTrigger {
return new BlueprintTrigger({
id: options?.id,
type: 'message',
condition: options?.condition,
priority: options?.priority,
callback
});
}
/**
* @zh 创建定时器触发器
* @en Create timer trigger
*/
export function createTimerTrigger(
callback?: TriggerCallback,
options?: { id?: string; condition?: ITriggerCondition; priority?: number }
): BlueprintTrigger {
return new BlueprintTrigger({
id: options?.id,
type: 'timer',
condition: options?.condition,
priority: options?.priority,
callback
});
}
/**
* @zh 创建状态进入触发器
* @en Create state enter trigger
*/
export function createStateEnterTrigger(
callback?: TriggerCallback,
options?: { id?: string; condition?: ITriggerCondition; priority?: number }
): BlueprintTrigger {
return new BlueprintTrigger({
id: options?.id,
type: 'stateEnter',
condition: options?.condition,
priority: options?.priority,
callback
});
}
/**
* @zh 创建状态退出触发器
* @en Create state exit trigger
*/
export function createStateExitTrigger(
callback?: TriggerCallback,
options?: { id?: string; condition?: ITriggerCondition; priority?: number }
): BlueprintTrigger {
return new BlueprintTrigger({
id: options?.id,
type: 'stateExit',
condition: options?.condition,
priority: options?.priority,
callback
});
}
/**
* @zh 创建自定义触发器
* @en Create custom trigger
*/
export function createCustomTrigger(
callback?: TriggerCallback,
options?: { id?: string; condition?: ITriggerCondition; priority?: number }
): BlueprintTrigger {
return new BlueprintTrigger({
id: options?.id,
type: 'custom',
condition: options?.condition,
priority: options?.priority,
callback
});
}

View File

@@ -0,0 +1,479 @@
/**
* @zh 触发器条件系统
* @en Trigger Condition System
*
* @zh 提供触发器触发前的条件检查能力
* @en Provides condition checking before trigger fires
*/
import type {
ITriggerContext,
TriggerType,
IInputTriggerContext,
IMessageTriggerContext,
IStateTriggerContext,
ITimerTriggerContext,
ICollisionTriggerContext,
ICustomTriggerContext
} from './TriggerTypes';
// =============================================================================
// 条件接口 | Condition Interface
// =============================================================================
/**
* @zh 触发器条件接口
* @en Trigger condition interface
*/
export interface ITriggerCondition {
/**
* @zh 条件类型标识
* @en Condition type identifier
*/
readonly type: string;
/**
* @zh 评估条件是否满足
* @en Evaluate if condition is met
*
* @param context - @zh 触发器上下文 @en Trigger context
* @returns @zh 条件是否满足 @en Whether condition is met
*/
evaluate(context: ITriggerContext): boolean;
}
/**
* @zh 条件组合逻辑
* @en Condition combination logic
*/
export type ConditionLogic = 'and' | 'or';
// =============================================================================
// 复合条件 | Composite Conditions
// =============================================================================
/**
* @zh 复合条件 - 组合多个条件
* @en Composite condition - combines multiple conditions
*/
export class CompositeCondition implements ITriggerCondition {
readonly type = 'composite';
constructor(
private readonly _conditions: ITriggerCondition[],
private readonly _logic: ConditionLogic = 'and'
) {}
evaluate(context: ITriggerContext): boolean {
if (this._conditions.length === 0) {
return true;
}
if (this._logic === 'and') {
return this._conditions.every(c => c.evaluate(context));
} else {
return this._conditions.some(c => c.evaluate(context));
}
}
}
/**
* @zh 非条件 - 取反
* @en Not condition - negates
*/
export class NotCondition implements ITriggerCondition {
readonly type = 'not';
constructor(private readonly _condition: ITriggerCondition) {}
evaluate(context: ITriggerContext): boolean {
return !this._condition.evaluate(context);
}
}
// =============================================================================
// 通用条件 | Generic Conditions
// =============================================================================
/**
* @zh 始终为真的条件
* @en Always true condition
*/
export class AlwaysTrueCondition implements ITriggerCondition {
readonly type = 'alwaysTrue';
evaluate(_context: ITriggerContext): boolean {
return true;
}
}
/**
* @zh 始终为假的条件
* @en Always false condition
*/
export class AlwaysFalseCondition implements ITriggerCondition {
readonly type = 'alwaysFalse';
evaluate(_context: ITriggerContext): boolean {
return false;
}
}
/**
* @zh 触发器类型条件
* @en Trigger type condition
*/
export class TriggerTypeCondition implements ITriggerCondition {
readonly type = 'triggerType';
constructor(private readonly _allowedTypes: TriggerType[]) {}
evaluate(context: ITriggerContext): boolean {
return this._allowedTypes.includes(context.type);
}
}
/**
* @zh 实体 ID 条件
* @en Entity ID condition
*/
export class EntityIdCondition implements ITriggerCondition {
readonly type = 'entityId';
constructor(
private readonly _entityId: string,
private readonly _checkSource: boolean = true
) {}
evaluate(context: ITriggerContext): boolean {
if (this._checkSource) {
return context.sourceEntityId === this._entityId;
}
return false;
}
}
/**
* @zh 自定义函数条件
* @en Custom function condition
*/
export class FunctionCondition implements ITriggerCondition {
readonly type = 'function';
constructor(
private readonly _predicate: (context: ITriggerContext) => boolean
) {}
evaluate(context: ITriggerContext): boolean {
return this._predicate(context);
}
}
// =============================================================================
// 特定类型条件 | Type-Specific Conditions
// =============================================================================
/**
* @zh 输入动作条件
* @en Input action condition
*/
export class InputActionCondition implements ITriggerCondition {
readonly type = 'inputAction';
constructor(
private readonly _action: string,
private readonly _checkPressed?: boolean,
private readonly _checkReleased?: boolean
) {}
evaluate(context: ITriggerContext): boolean {
if (context.type !== 'input') {
return false;
}
const inputContext = context as unknown as IInputTriggerContext;
if (inputContext.action !== this._action) {
return false;
}
if (this._checkPressed !== undefined && inputContext.pressed !== this._checkPressed) {
return false;
}
if (this._checkReleased !== undefined && inputContext.released !== this._checkReleased) {
return false;
}
return true;
}
}
/**
* @zh 消息名称条件
* @en Message name condition
*/
export class MessageNameCondition implements ITriggerCondition {
readonly type = 'messageName';
constructor(private readonly _messageName: string) {}
evaluate(context: ITriggerContext): boolean {
if (context.type !== 'message') {
return false;
}
const messageContext = context as unknown as IMessageTriggerContext;
return messageContext.messageName === this._messageName;
}
}
/**
* @zh 状态名称条件
* @en State name condition
*/
export class StateNameCondition implements ITriggerCondition {
readonly type = 'stateName';
constructor(
private readonly _stateName: string,
private readonly _checkCurrent: boolean = true
) {}
evaluate(context: ITriggerContext): boolean {
if (context.type !== 'stateEnter' && context.type !== 'stateExit') {
return false;
}
const stateContext = context as unknown as IStateTriggerContext;
if (this._checkCurrent) {
return stateContext.currentState === this._stateName;
} else {
return stateContext.previousState === this._stateName;
}
}
}
/**
* @zh 定时器 ID 条件
* @en Timer ID condition
*/
export class TimerIdCondition implements ITriggerCondition {
readonly type = 'timerId';
constructor(private readonly _timerId: string) {}
evaluate(context: ITriggerContext): boolean {
if (context.type !== 'timer') {
return false;
}
const timerContext = context as unknown as ITimerTriggerContext;
return timerContext.timerId === this._timerId;
}
}
/**
* @zh 碰撞实体条件
* @en Collision entity condition
*/
export class CollisionEntityCondition implements ITriggerCondition {
readonly type = 'collisionEntity';
constructor(
private readonly _otherEntityId?: string,
private readonly _checkEnter?: boolean,
private readonly _checkExit?: boolean
) {}
evaluate(context: ITriggerContext): boolean {
if (context.type !== 'collision') {
return false;
}
const collisionContext = context as unknown as ICollisionTriggerContext;
if (this._otherEntityId !== undefined && collisionContext.otherEntityId !== this._otherEntityId) {
return false;
}
if (this._checkEnter !== undefined && collisionContext.isEnter !== this._checkEnter) {
return false;
}
if (this._checkExit !== undefined && collisionContext.isExit !== this._checkExit) {
return false;
}
return true;
}
}
/**
* @zh 自定义事件名称条件
* @en Custom event name condition
*/
export class CustomEventCondition implements ITriggerCondition {
readonly type = 'customEvent';
constructor(private readonly _eventName: string) {}
evaluate(context: ITriggerContext): boolean {
if (context.type !== 'custom') {
return false;
}
const customContext = context as unknown as ICustomTriggerContext;
return customContext.eventName === this._eventName;
}
}
// =============================================================================
// 条件构建器 | Condition Builder
// =============================================================================
/**
* @zh 条件构建器 - 链式 API
* @en Condition builder - fluent API
*/
export class ConditionBuilder {
private _conditions: ITriggerCondition[] = [];
private _logic: ConditionLogic = 'and';
/**
* @zh 设置组合逻辑为 AND
* @en Set combination logic to AND
*/
and(): this {
this._logic = 'and';
return this;
}
/**
* @zh 设置组合逻辑为 OR
* @en Set combination logic to OR
*/
or(): this {
this._logic = 'or';
return this;
}
/**
* @zh 添加触发器类型条件
* @en Add trigger type condition
*/
ofType(...types: TriggerType[]): this {
this._conditions.push(new TriggerTypeCondition(types));
return this;
}
/**
* @zh 添加实体 ID 条件
* @en Add entity ID condition
*/
fromEntity(entityId: string): this {
this._conditions.push(new EntityIdCondition(entityId));
return this;
}
/**
* @zh 添加输入动作条件
* @en Add input action condition
*/
onInput(action: string, options?: { pressed?: boolean; released?: boolean }): this {
this._conditions.push(new InputActionCondition(action, options?.pressed, options?.released));
return this;
}
/**
* @zh 添加消息条件
* @en Add message condition
*/
onMessage(messageName: string): this {
this._conditions.push(new MessageNameCondition(messageName));
return this;
}
/**
* @zh 添加状态条件
* @en Add state condition
*/
onState(stateName: string, checkCurrent: boolean = true): this {
this._conditions.push(new StateNameCondition(stateName, checkCurrent));
return this;
}
/**
* @zh 添加定时器条件
* @en Add timer condition
*/
onTimer(timerId: string): this {
this._conditions.push(new TimerIdCondition(timerId));
return this;
}
/**
* @zh 添加碰撞条件
* @en Add collision condition
*/
onCollision(options?: { entityId?: string; isEnter?: boolean; isExit?: boolean }): this {
this._conditions.push(new CollisionEntityCondition(
options?.entityId,
options?.isEnter,
options?.isExit
));
return this;
}
/**
* @zh 添加自定义事件条件
* @en Add custom event condition
*/
onCustomEvent(eventName: string): this {
this._conditions.push(new CustomEventCondition(eventName));
return this;
}
/**
* @zh 添加自定义函数条件
* @en Add custom function condition
*/
where(predicate: (context: ITriggerContext) => boolean): this {
this._conditions.push(new FunctionCondition(predicate));
return this;
}
/**
* @zh 添加取反条件
* @en Add negated condition
*/
not(condition: ITriggerCondition): this {
this._conditions.push(new NotCondition(condition));
return this;
}
/**
* @zh 构建条件
* @en Build condition
*/
build(): ITriggerCondition {
if (this._conditions.length === 0) {
return new AlwaysTrueCondition();
}
if (this._conditions.length === 1) {
return this._conditions[0];
}
return new CompositeCondition(this._conditions, this._logic);
}
}
/**
* @zh 创建条件构建器
* @en Create condition builder
*/
export function condition(): ConditionBuilder {
return new ConditionBuilder();
}

View File

@@ -0,0 +1,461 @@
/**
* @zh 触发器调度器
* @en Trigger Dispatcher
*
* @zh 负责分发触发器事件到订阅者
* @en Responsible for dispatching trigger events to subscribers
*/
import type { TriggerType, ITriggerContext } from './TriggerTypes';
import type { IBlueprintTrigger, ITriggerRegistry, TriggerCallback } from './BlueprintTrigger';
import { TriggerRegistry } from './BlueprintTrigger';
// =============================================================================
// 调度器接口 | Dispatcher Interface
// =============================================================================
/**
* @zh 触发结果
* @en Trigger result
*/
export interface TriggerResult {
/**
* @zh 触发器 ID
* @en Trigger ID
*/
triggerId: string;
/**
* @zh 是否成功
* @en Is successful
*/
success: boolean;
/**
* @zh 错误信息
* @en Error message
*/
error?: string;
}
/**
* @zh 调度结果
* @en Dispatch result
*/
export interface DispatchResult {
/**
* @zh 上下文
* @en Context
*/
context: ITriggerContext;
/**
* @zh 触发的触发器数量
* @en Number of triggers fired
*/
triggeredCount: number;
/**
* @zh 各触发器结果
* @en Results of each trigger
*/
results: TriggerResult[];
}
/**
* @zh 触发器调度器接口
* @en Trigger dispatcher interface
*/
export interface ITriggerDispatcher {
/**
* @zh 调度触发器
* @en Dispatch trigger
*/
dispatch(context: ITriggerContext): DispatchResult;
/**
* @zh 异步调度触发器
* @en Async dispatch trigger
*/
dispatchAsync(context: ITriggerContext): Promise<DispatchResult>;
/**
* @zh 订阅触发器类型
* @en Subscribe to trigger type
*/
subscribe(type: TriggerType, callback: TriggerCallback): () => void;
/**
* @zh 取消订阅
* @en Unsubscribe
*/
unsubscribe(type: TriggerType, callback: TriggerCallback): void;
/**
* @zh 获取注册表
* @en Get registry
*/
readonly registry: ITriggerRegistry;
}
// =============================================================================
// 调度器实现 | Dispatcher Implementation
// =============================================================================
/**
* @zh 触发器调度器实现
* @en Trigger dispatcher implementation
*/
export class TriggerDispatcher implements ITriggerDispatcher {
private readonly _registry: ITriggerRegistry;
private readonly _typeSubscribers: Map<TriggerType, Set<TriggerCallback>> = new Map();
private readonly _globalSubscribers: Set<TriggerCallback> = new Set();
private _isDispatching: boolean = false;
private _pendingContexts: ITriggerContext[] = [];
constructor(registry?: ITriggerRegistry) {
this._registry = registry ?? new TriggerRegistry();
}
get registry(): ITriggerRegistry {
return this._registry;
}
/**
* @zh 调度触发器
* @en Dispatch trigger
*/
dispatch(context: ITriggerContext): DispatchResult {
if (this._isDispatching) {
this._pendingContexts.push(context);
return {
context,
triggeredCount: 0,
results: []
};
}
this._isDispatching = true;
try {
const result = this._doDispatch(context);
while (this._pendingContexts.length > 0) {
const pendingContext = this._pendingContexts.shift()!;
this._doDispatch(pendingContext);
}
return result;
} finally {
this._isDispatching = false;
}
}
/**
* @zh 执行调度
* @en Do dispatch
*/
private _doDispatch(context: ITriggerContext): DispatchResult {
const results: TriggerResult[] = [];
let triggeredCount = 0;
const triggers = this._registry.getByType(context.type);
for (const trigger of triggers) {
if (trigger.shouldFire(context)) {
try {
trigger.fire(context);
triggeredCount++;
results.push({
triggerId: trigger.id,
success: true
});
} catch (error) {
results.push({
triggerId: trigger.id,
success: false,
error: error instanceof Error ? error.message : String(error)
});
}
}
}
this._notifySubscribers(context);
return {
context,
triggeredCount,
results
};
}
/**
* @zh 通知订阅者
* @en Notify subscribers
*/
private _notifySubscribers(context: ITriggerContext): void {
const typeSubscribers = this._typeSubscribers.get(context.type);
if (typeSubscribers) {
for (const callback of typeSubscribers) {
try {
callback(context);
} catch (error) {
console.error(`Trigger subscriber error: ${error}`);
}
}
}
for (const callback of this._globalSubscribers) {
try {
callback(context);
} catch (error) {
console.error(`Global trigger subscriber error: ${error}`);
}
}
}
/**
* @zh 异步调度触发器
* @en Async dispatch trigger
*/
async dispatchAsync(context: ITriggerContext): Promise<DispatchResult> {
return new Promise((resolve) => {
queueMicrotask(() => {
resolve(this.dispatch(context));
});
});
}
/**
* @zh 订阅触发器类型
* @en Subscribe to trigger type
*/
subscribe(type: TriggerType, callback: TriggerCallback): () => void {
if (!this._typeSubscribers.has(type)) {
this._typeSubscribers.set(type, new Set());
}
this._typeSubscribers.get(type)!.add(callback);
return () => this.unsubscribe(type, callback);
}
/**
* @zh 取消订阅
* @en Unsubscribe
*/
unsubscribe(type: TriggerType, callback: TriggerCallback): void {
const subscribers = this._typeSubscribers.get(type);
if (subscribers) {
subscribers.delete(callback);
}
}
/**
* @zh 订阅所有触发器
* @en Subscribe to all triggers
*/
subscribeAll(callback: TriggerCallback): () => void {
this._globalSubscribers.add(callback);
return () => this.unsubscribeAll(callback);
}
/**
* @zh 取消订阅所有
* @en Unsubscribe from all
*/
unsubscribeAll(callback: TriggerCallback): void {
this._globalSubscribers.delete(callback);
}
/**
* @zh 清除所有订阅
* @en Clear all subscriptions
*/
clearSubscriptions(): void {
this._typeSubscribers.clear();
this._globalSubscribers.clear();
}
}
// =============================================================================
// 实体触发器管理器 | Entity Trigger Manager
// =============================================================================
/**
* @zh 实体触发器管理器接口
* @en Entity trigger manager interface
*/
export interface IEntityTriggerManager {
/**
* @zh 为实体注册触发器
* @en Register trigger for entity
*/
registerForEntity(entityId: string, trigger: IBlueprintTrigger): void;
/**
* @zh 注销实体的触发器
* @en Unregister trigger from entity
*/
unregisterFromEntity(entityId: string, triggerId: string): boolean;
/**
* @zh 获取实体的所有触发器
* @en Get all triggers for entity
*/
getEntityTriggers(entityId: string): IBlueprintTrigger[];
/**
* @zh 清除实体的所有触发器
* @en Clear all triggers for entity
*/
clearEntityTriggers(entityId: string): void;
/**
* @zh 调度器
* @en Dispatcher
*/
readonly dispatcher: ITriggerDispatcher;
}
/**
* @zh 实体触发器管理器实现
* @en Entity trigger manager implementation
*/
export class EntityTriggerManager implements IEntityTriggerManager {
private readonly _dispatcher: ITriggerDispatcher;
private readonly _entityTriggers: Map<string, Set<string>> = new Map();
constructor(dispatcher?: ITriggerDispatcher) {
this._dispatcher = dispatcher ?? new TriggerDispatcher();
}
get dispatcher(): ITriggerDispatcher {
return this._dispatcher;
}
/**
* @zh 为实体注册触发器
* @en Register trigger for entity
*/
registerForEntity(entityId: string, trigger: IBlueprintTrigger): void {
this._dispatcher.registry.register(trigger);
if (!this._entityTriggers.has(entityId)) {
this._entityTriggers.set(entityId, new Set());
}
this._entityTriggers.get(entityId)!.add(trigger.id);
}
/**
* @zh 注销实体的触发器
* @en Unregister trigger from entity
*/
unregisterFromEntity(entityId: string, triggerId: string): boolean {
const entitySet = this._entityTriggers.get(entityId);
if (!entitySet) {
return false;
}
if (!entitySet.has(triggerId)) {
return false;
}
entitySet.delete(triggerId);
return this._dispatcher.registry.unregister(triggerId);
}
/**
* @zh 获取实体的所有触发器
* @en Get all triggers for entity
*/
getEntityTriggers(entityId: string): IBlueprintTrigger[] {
const entitySet = this._entityTriggers.get(entityId);
if (!entitySet) {
return [];
}
const triggers: IBlueprintTrigger[] = [];
for (const triggerId of entitySet) {
const trigger = this._dispatcher.registry.get(triggerId);
if (trigger) {
triggers.push(trigger);
}
}
return triggers;
}
/**
* @zh 清除实体的所有触发器
* @en Clear all triggers for entity
*/
clearEntityTriggers(entityId: string): void {
const entitySet = this._entityTriggers.get(entityId);
if (!entitySet) {
return;
}
for (const triggerId of entitySet) {
this._dispatcher.registry.unregister(triggerId);
}
this._entityTriggers.delete(entityId);
}
/**
* @zh 调度触发器到实体
* @en Dispatch trigger to entity
*/
dispatchToEntity(entityId: string, context: ITriggerContext): DispatchResult {
const entityTriggers = this.getEntityTriggers(entityId);
const results: TriggerResult[] = [];
let triggeredCount = 0;
for (const trigger of entityTriggers) {
if (trigger.shouldFire(context)) {
try {
trigger.fire(context);
triggeredCount++;
results.push({
triggerId: trigger.id,
success: true
});
} catch (error) {
results.push({
triggerId: trigger.id,
success: false,
error: error instanceof Error ? error.message : String(error)
});
}
}
}
return {
context,
triggeredCount,
results
};
}
}
// =============================================================================
// 工厂函数 | Factory Functions
// =============================================================================
/**
* @zh 创建触发器调度器
* @en Create trigger dispatcher
*/
export function createTriggerDispatcher(registry?: ITriggerRegistry): TriggerDispatcher {
return new TriggerDispatcher(registry);
}
/**
* @zh 创建实体触发器管理器
* @en Create entity trigger manager
*/
export function createEntityTriggerManager(dispatcher?: ITriggerDispatcher): EntityTriggerManager {
return new EntityTriggerManager(dispatcher);
}

View File

@@ -0,0 +1,400 @@
/**
* @zh 蓝图触发器类型定义
* @en Blueprint Trigger Type Definitions
*
* @zh 定义触发器的核心类型和接口
* @en Defines core types and interfaces for triggers
*/
// =============================================================================
// 触发器类型 | Trigger Types
// =============================================================================
/**
* @zh 触发器类型枚举
* @en Trigger type enumeration
*/
export type TriggerType =
| 'tick' // 每帧触发 | Every frame
| 'input' // 输入事件 | Input event
| 'collision' // 碰撞事件 | Collision event
| 'message' // 消息事件 | Message event
| 'timer' // 定时器事件 | Timer event
| 'stateEnter' // 状态进入 | State enter
| 'stateExit' // 状态退出 | State exit
| 'custom'; // 自定义事件 | Custom event
/**
* @zh 触发器类型常量
* @en Trigger type constants
*/
export const TriggerTypes = {
TICK: 'tick' as const,
INPUT: 'input' as const,
COLLISION: 'collision' as const,
MESSAGE: 'message' as const,
TIMER: 'timer' as const,
STATE_ENTER: 'stateEnter' as const,
STATE_EXIT: 'stateExit' as const,
CUSTOM: 'custom' as const
} as const;
// =============================================================================
// 触发器上下文 | Trigger Context
// =============================================================================
/**
* @zh 触发器上下文基础接口
* @en Trigger context base interface
*/
export interface ITriggerContext {
/**
* @zh 触发器类型
* @en Trigger type
*/
readonly type: TriggerType;
/**
* @zh 触发时间戳
* @en Trigger timestamp
*/
readonly timestamp: number;
/**
* @zh 触发源实体 ID
* @en Source entity ID
*/
readonly sourceEntityId?: string;
/**
* @zh 附加数据
* @en Additional data
*/
readonly data?: Record<string, unknown>;
}
/**
* @zh Tick 触发器上下文
* @en Tick trigger context
*/
export interface ITickTriggerContext extends ITriggerContext {
readonly type: 'tick';
/**
* @zh 增量时间(秒)
* @en Delta time (seconds)
*/
readonly deltaTime: number;
/**
* @zh 帧计数
* @en Frame count
*/
readonly frameCount: number;
}
/**
* @zh 输入触发器上下文
* @en Input trigger context
*/
export interface IInputTriggerContext extends ITriggerContext {
readonly type: 'input';
/**
* @zh 输入动作名称
* @en Input action name
*/
readonly action: string;
/**
* @zh 输入值
* @en Input value
*/
readonly value: number | boolean;
/**
* @zh 是否刚按下
* @en Is just pressed
*/
readonly pressed?: boolean;
/**
* @zh 是否刚释放
* @en Is just released
*/
readonly released?: boolean;
}
/**
* @zh 碰撞触发器上下文
* @en Collision trigger context
*/
export interface ICollisionTriggerContext extends ITriggerContext {
readonly type: 'collision';
/**
* @zh 碰撞的另一个实体 ID
* @en Other entity ID in collision
*/
readonly otherEntityId: string;
/**
* @zh 碰撞点
* @en Collision point
*/
readonly point?: { x: number; y: number };
/**
* @zh 碰撞法线
* @en Collision normal
*/
readonly normal?: { x: number; y: number };
/**
* @zh 是否开始碰撞
* @en Is collision start
*/
readonly isEnter: boolean;
/**
* @zh 是否结束碰撞
* @en Is collision end
*/
readonly isExit: boolean;
}
/**
* @zh 消息触发器上下文
* @en Message trigger context
*/
export interface IMessageTriggerContext extends ITriggerContext {
readonly type: 'message';
/**
* @zh 消息名称
* @en Message name
*/
readonly messageName: string;
/**
* @zh 发送者 ID
* @en Sender ID
*/
readonly senderId?: string;
/**
* @zh 消息负载
* @en Message payload
*/
readonly payload?: unknown;
}
/**
* @zh 定时器触发器上下文
* @en Timer trigger context
*/
export interface ITimerTriggerContext extends ITriggerContext {
readonly type: 'timer';
/**
* @zh 定时器 ID
* @en Timer ID
*/
readonly timerId: string;
/**
* @zh 是否循环触发
* @en Is repeating
*/
readonly isRepeating: boolean;
/**
* @zh 已触发次数
* @en Times fired
*/
readonly timesFired: number;
}
/**
* @zh 状态触发器上下文
* @en State trigger context
*/
export interface IStateTriggerContext extends ITriggerContext {
readonly type: 'stateEnter' | 'stateExit';
/**
* @zh 状态机 ID
* @en State machine ID
*/
readonly stateMachineId: string;
/**
* @zh 当前状态
* @en Current state
*/
readonly currentState: string;
/**
* @zh 之前状态
* @en Previous state
*/
readonly previousState?: string;
}
/**
* @zh 自定义触发器上下文
* @en Custom trigger context
*/
export interface ICustomTriggerContext extends ITriggerContext {
readonly type: 'custom';
/**
* @zh 事件名称
* @en Event name
*/
readonly eventName: string;
}
/**
* @zh 所有触发器上下文的联合类型
* @en Union type of all trigger contexts
*/
export type TriggerContext =
| ITickTriggerContext
| IInputTriggerContext
| ICollisionTriggerContext
| IMessageTriggerContext
| ITimerTriggerContext
| IStateTriggerContext
| ICustomTriggerContext;
// =============================================================================
// 工厂函数 | Factory Functions
// =============================================================================
/**
* @zh 创建 Tick 触发器上下文
* @en Create tick trigger context
*/
export function createTickContext(
deltaTime: number,
frameCount: number,
sourceEntityId?: string
): ITickTriggerContext {
return {
type: 'tick',
timestamp: Date.now(),
deltaTime,
frameCount,
sourceEntityId
};
}
/**
* @zh 创建输入触发器上下文
* @en Create input trigger context
*/
export function createInputContext(
action: string,
value: number | boolean,
options?: {
pressed?: boolean;
released?: boolean;
sourceEntityId?: string;
}
): IInputTriggerContext {
return {
type: 'input',
timestamp: Date.now(),
action,
value,
pressed: options?.pressed,
released: options?.released,
sourceEntityId: options?.sourceEntityId
};
}
/**
* @zh 创建碰撞触发器上下文
* @en Create collision trigger context
*/
export function createCollisionContext(
otherEntityId: string,
isEnter: boolean,
options?: {
point?: { x: number; y: number };
normal?: { x: number; y: number };
sourceEntityId?: string;
}
): ICollisionTriggerContext {
return {
type: 'collision',
timestamp: Date.now(),
otherEntityId,
isEnter,
isExit: !isEnter,
point: options?.point,
normal: options?.normal,
sourceEntityId: options?.sourceEntityId
};
}
/**
* @zh 创建消息触发器上下文
* @en Create message trigger context
*/
export function createMessageContext(
messageName: string,
payload?: unknown,
options?: {
senderId?: string;
sourceEntityId?: string;
}
): IMessageTriggerContext {
return {
type: 'message',
timestamp: Date.now(),
messageName,
payload,
senderId: options?.senderId,
sourceEntityId: options?.sourceEntityId
};
}
/**
* @zh 创建定时器触发器上下文
* @en Create timer trigger context
*/
export function createTimerContext(
timerId: string,
isRepeating: boolean,
timesFired: number,
sourceEntityId?: string
): ITimerTriggerContext {
return {
type: 'timer',
timestamp: Date.now(),
timerId,
isRepeating,
timesFired,
sourceEntityId
};
}
/**
* @zh 创建状态触发器上下文
* @en Create state trigger context
*/
export function createStateContext(
type: 'stateEnter' | 'stateExit',
stateMachineId: string,
currentState: string,
previousState?: string,
sourceEntityId?: string
): IStateTriggerContext {
return {
type,
timestamp: Date.now(),
stateMachineId,
currentState,
previousState,
sourceEntityId
};
}
/**
* @zh 创建自定义触发器上下文
* @en Create custom trigger context
*/
export function createCustomContext(
eventName: string,
data?: Record<string, unknown>,
sourceEntityId?: string
): ICustomTriggerContext {
return {
type: 'custom',
timestamp: Date.now(),
eventName,
data,
sourceEntityId
};
}

View File

@@ -0,0 +1,105 @@
/**
* @zh 蓝图触发器模块
* @en Blueprint Triggers Module
*
* @zh 提供蓝图触发器系统的所有导出
* @en Provides all exports for the blueprint trigger system
*/
// =============================================================================
// 触发器类型 | Trigger Types
// =============================================================================
export type {
TriggerType,
ITriggerContext,
ITickTriggerContext,
IInputTriggerContext,
ICollisionTriggerContext,
IMessageTriggerContext,
ITimerTriggerContext,
IStateTriggerContext,
ICustomTriggerContext,
TriggerContext
} from './TriggerTypes';
export {
TriggerTypes,
createTickContext,
createInputContext,
createCollisionContext,
createMessageContext,
createTimerContext,
createStateContext,
createCustomContext
} from './TriggerTypes';
// =============================================================================
// 触发器条件 | Trigger Conditions
// =============================================================================
export type {
ITriggerCondition,
ConditionLogic
} from './TriggerCondition';
export {
CompositeCondition,
NotCondition,
AlwaysTrueCondition,
AlwaysFalseCondition,
TriggerTypeCondition,
EntityIdCondition,
FunctionCondition,
InputActionCondition,
MessageNameCondition,
StateNameCondition,
TimerIdCondition,
CollisionEntityCondition,
CustomEventCondition,
ConditionBuilder,
condition
} from './TriggerCondition';
// =============================================================================
// 蓝图触发器 | Blueprint Trigger
// =============================================================================
export type {
TriggerCallback,
IBlueprintTrigger,
TriggerConfig,
ITriggerRegistry
} from './BlueprintTrigger';
export {
BlueprintTrigger,
TriggerRegistry,
createTrigger,
createTickTrigger,
createInputTrigger,
createCollisionTrigger,
createMessageTrigger,
createTimerTrigger,
createStateEnterTrigger,
createStateExitTrigger,
createCustomTrigger
} from './BlueprintTrigger';
// =============================================================================
// 触发器调度器 | Trigger Dispatcher
// =============================================================================
export type {
TriggerResult,
DispatchResult,
ITriggerDispatcher,
IEntityTriggerManager
} from './TriggerDispatcher';
export {
TriggerDispatcher,
EntityTriggerManager,
createTriggerDispatcher,
createEntityTriggerManager
} from './TriggerDispatcher';