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:
23
packages/framework/timer/module.json
Normal file
23
packages/framework/timer/module.json
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"id": "timer",
|
||||
"name": "@esengine/timer",
|
||||
"globalKey": "timer",
|
||||
"displayName": "Timer & Cooldown",
|
||||
"description": "定时器和冷却系统 | Timer and cooldown system",
|
||||
"version": "1.0.0",
|
||||
"category": "Other",
|
||||
"icon": "Timer",
|
||||
"tags": ["timer", "cooldown", "delay", "schedule"],
|
||||
"isCore": false,
|
||||
"defaultEnabled": true,
|
||||
"isEngineModule": true,
|
||||
"canContainContent": false,
|
||||
"platforms": ["web", "desktop"],
|
||||
"dependencies": ["core"],
|
||||
"exports": {
|
||||
"components": [],
|
||||
"systems": []
|
||||
},
|
||||
"outputPath": "dist/index.js",
|
||||
"pluginExport": "TimerPlugin"
|
||||
}
|
||||
40
packages/framework/timer/package.json
Normal file
40
packages/framework/timer/package.json
Normal file
@@ -0,0 +1,40 @@
|
||||
{
|
||||
"name": "@esengine/timer",
|
||||
"version": "1.0.0",
|
||||
"description": "Timer and cooldown system for ECS Framework / ECS 框架的定时器和冷却系统",
|
||||
"type": "module",
|
||||
"main": "./dist/index.js",
|
||||
"module": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
"exports": {
|
||||
".": {
|
||||
"import": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts"
|
||||
}
|
||||
},
|
||||
"files": [
|
||||
"dist",
|
||||
"module.json"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "tsup",
|
||||
"build:watch": "tsup --watch",
|
||||
"type-check": "tsc --noEmit",
|
||||
"clean": "rimraf dist"
|
||||
},
|
||||
"dependencies": {
|
||||
"tslib": "^2.8.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@esengine/ecs-framework": "workspace:*",
|
||||
"@esengine/blueprint": "workspace:*",
|
||||
"@esengine/build-config": "workspace:*",
|
||||
"@types/node": "^20.19.17",
|
||||
"rimraf": "^5.0.0",
|
||||
"tsup": "^8.0.0",
|
||||
"typescript": "^5.8.3"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
}
|
||||
}
|
||||
294
packages/framework/timer/src/ITimerService.ts
Normal file
294
packages/framework/timer/src/ITimerService.ts
Normal file
@@ -0,0 +1,294 @@
|
||||
/**
|
||||
* @zh 定时器服务接口
|
||||
* @en Timer Service Interfaces
|
||||
*
|
||||
* @zh 提供定时器和冷却系统的核心接口
|
||||
* @en Provides core interfaces for timer and cooldown systems
|
||||
*/
|
||||
|
||||
// =============================================================================
|
||||
// 定时器句柄 | Timer Handle
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* @zh 定时器句柄,用于取消定时器
|
||||
* @en Timer handle for cancelling timers
|
||||
*/
|
||||
export interface TimerHandle {
|
||||
/**
|
||||
* @zh 定时器 ID
|
||||
* @en Timer ID
|
||||
*/
|
||||
readonly id: string;
|
||||
|
||||
/**
|
||||
* @zh 是否有效(未被取消)
|
||||
* @en Whether the timer is still valid (not cancelled)
|
||||
*/
|
||||
readonly isValid: boolean;
|
||||
|
||||
/**
|
||||
* @zh 取消定时器
|
||||
* @en Cancel the timer
|
||||
*/
|
||||
cancel(): void;
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// 定时器信息 | Timer Info
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* @zh 定时器信息
|
||||
* @en Timer information
|
||||
*/
|
||||
export interface TimerInfo {
|
||||
/**
|
||||
* @zh 定时器 ID
|
||||
* @en Timer ID
|
||||
*/
|
||||
readonly id: string;
|
||||
|
||||
/**
|
||||
* @zh 剩余时间(毫秒)
|
||||
* @en Remaining time in milliseconds
|
||||
*/
|
||||
readonly remaining: number;
|
||||
|
||||
/**
|
||||
* @zh 是否重复执行
|
||||
* @en Whether the timer repeats
|
||||
*/
|
||||
readonly repeating: boolean;
|
||||
|
||||
/**
|
||||
* @zh 间隔时间(毫秒,仅重复定时器)
|
||||
* @en Interval in milliseconds (only for repeating timers)
|
||||
*/
|
||||
readonly interval?: number;
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// 冷却信息 | Cooldown Info
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* @zh 冷却信息
|
||||
* @en Cooldown information
|
||||
*/
|
||||
export interface CooldownInfo {
|
||||
/**
|
||||
* @zh 冷却 ID
|
||||
* @en Cooldown ID
|
||||
*/
|
||||
readonly id: string;
|
||||
|
||||
/**
|
||||
* @zh 总持续时间(毫秒)
|
||||
* @en Total duration in milliseconds
|
||||
*/
|
||||
readonly duration: number;
|
||||
|
||||
/**
|
||||
* @zh 剩余时间(毫秒)
|
||||
* @en Remaining time in milliseconds
|
||||
*/
|
||||
readonly remaining: number;
|
||||
|
||||
/**
|
||||
* @zh 进度(0-1,0 表示刚开始,1 表示结束)
|
||||
* @en Progress from 0 to 1 (0 = just started, 1 = finished)
|
||||
*/
|
||||
readonly progress: number;
|
||||
|
||||
/**
|
||||
* @zh 是否已就绪(冷却完成)
|
||||
* @en Whether the cooldown is ready (finished)
|
||||
*/
|
||||
readonly isReady: boolean;
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// 定时器回调 | Timer Callbacks
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* @zh 定时器回调函数
|
||||
* @en Timer callback function
|
||||
*/
|
||||
export type TimerCallback = () => void;
|
||||
|
||||
/**
|
||||
* @zh 带时间参数的定时器回调
|
||||
* @en Timer callback with time parameter
|
||||
*/
|
||||
export type TimerCallbackWithTime = (deltaTime: number) => void;
|
||||
|
||||
// =============================================================================
|
||||
// 定时器服务接口 | Timer Service Interface
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* @zh 定时器服务接口
|
||||
* @en Timer service interface
|
||||
*
|
||||
* @zh 提供定时器调度和冷却管理功能
|
||||
* @en Provides timer scheduling and cooldown management
|
||||
*/
|
||||
export interface ITimerService {
|
||||
// =========================================================================
|
||||
// 定时器 API | Timer API
|
||||
// =========================================================================
|
||||
|
||||
/**
|
||||
* @zh 调度一次性定时器
|
||||
* @en Schedule a one-time timer
|
||||
*
|
||||
* @param id - @zh 定时器标识 @en Timer identifier
|
||||
* @param delay - @zh 延迟时间(毫秒)@en Delay in milliseconds
|
||||
* @param callback - @zh 回调函数 @en Callback function
|
||||
* @returns @zh 定时器句柄 @en Timer handle
|
||||
*/
|
||||
schedule(id: string, delay: number, callback: TimerCallback): TimerHandle;
|
||||
|
||||
/**
|
||||
* @zh 调度重复定时器
|
||||
* @en Schedule a repeating timer
|
||||
*
|
||||
* @param id - @zh 定时器标识 @en Timer identifier
|
||||
* @param interval - @zh 间隔时间(毫秒)@en Interval in milliseconds
|
||||
* @param callback - @zh 回调函数 @en Callback function
|
||||
* @param immediate - @zh 是否立即执行一次 @en Whether to execute immediately
|
||||
* @returns @zh 定时器句柄 @en Timer handle
|
||||
*/
|
||||
scheduleRepeating(
|
||||
id: string,
|
||||
interval: number,
|
||||
callback: TimerCallback,
|
||||
immediate?: boolean
|
||||
): TimerHandle;
|
||||
|
||||
/**
|
||||
* @zh 取消定时器
|
||||
* @en Cancel a timer
|
||||
*
|
||||
* @param handle - @zh 定时器句柄 @en Timer handle
|
||||
*/
|
||||
cancel(handle: TimerHandle): void;
|
||||
|
||||
/**
|
||||
* @zh 通过 ID 取消定时器
|
||||
* @en Cancel timer by ID
|
||||
*
|
||||
* @param id - @zh 定时器标识 @en Timer identifier
|
||||
*/
|
||||
cancelById(id: string): void;
|
||||
|
||||
/**
|
||||
* @zh 检查定时器是否存在
|
||||
* @en Check if a timer exists
|
||||
*
|
||||
* @param id - @zh 定时器标识 @en Timer identifier
|
||||
* @returns @zh 是否存在 @en Whether the timer exists
|
||||
*/
|
||||
hasTimer(id: string): boolean;
|
||||
|
||||
/**
|
||||
* @zh 获取定时器信息
|
||||
* @en Get timer information
|
||||
*
|
||||
* @param id - @zh 定时器标识 @en Timer identifier
|
||||
* @returns @zh 定时器信息或 null @en Timer info or null
|
||||
*/
|
||||
getTimerInfo(id: string): TimerInfo | null;
|
||||
|
||||
// =========================================================================
|
||||
// 冷却 API | Cooldown API
|
||||
// =========================================================================
|
||||
|
||||
/**
|
||||
* @zh 开始冷却
|
||||
* @en Start a cooldown
|
||||
*
|
||||
* @param id - @zh 冷却标识 @en Cooldown identifier
|
||||
* @param duration - @zh 持续时间(毫秒)@en Duration in milliseconds
|
||||
*/
|
||||
startCooldown(id: string, duration: number): void;
|
||||
|
||||
/**
|
||||
* @zh 检查是否在冷却中
|
||||
* @en Check if on cooldown
|
||||
*
|
||||
* @param id - @zh 冷却标识 @en Cooldown identifier
|
||||
* @returns @zh 是否在冷却中 @en Whether on cooldown
|
||||
*/
|
||||
isOnCooldown(id: string): boolean;
|
||||
|
||||
/**
|
||||
* @zh 检查冷却是否就绪
|
||||
* @en Check if cooldown is ready
|
||||
*
|
||||
* @param id - @zh 冷却标识 @en Cooldown identifier
|
||||
* @returns @zh 是否已就绪 @en Whether ready
|
||||
*/
|
||||
isCooldownReady(id: string): boolean;
|
||||
|
||||
/**
|
||||
* @zh 获取剩余冷却时间
|
||||
* @en Get remaining cooldown time
|
||||
*
|
||||
* @param id - @zh 冷却标识 @en Cooldown identifier
|
||||
* @returns @zh 剩余时间(毫秒),0 表示无冷却 @en Remaining time in ms, 0 if no cooldown
|
||||
*/
|
||||
getCooldownRemaining(id: string): number;
|
||||
|
||||
/**
|
||||
* @zh 获取冷却进度
|
||||
* @en Get cooldown progress
|
||||
*
|
||||
* @param id - @zh 冷却标识 @en Cooldown identifier
|
||||
* @returns @zh 进度(0-1),1 表示完成或无冷却 @en Progress 0-1, 1 if done or no cooldown
|
||||
*/
|
||||
getCooldownProgress(id: string): number;
|
||||
|
||||
/**
|
||||
* @zh 获取冷却信息
|
||||
* @en Get cooldown information
|
||||
*
|
||||
* @param id - @zh 冷却标识 @en Cooldown identifier
|
||||
* @returns @zh 冷却信息或 null @en Cooldown info or null
|
||||
*/
|
||||
getCooldownInfo(id: string): CooldownInfo | null;
|
||||
|
||||
/**
|
||||
* @zh 重置冷却
|
||||
* @en Reset a cooldown
|
||||
*
|
||||
* @param id - @zh 冷却标识 @en Cooldown identifier
|
||||
*/
|
||||
resetCooldown(id: string): void;
|
||||
|
||||
/**
|
||||
* @zh 清除所有冷却
|
||||
* @en Clear all cooldowns
|
||||
*/
|
||||
clearAllCooldowns(): void;
|
||||
|
||||
// =========================================================================
|
||||
// 更新 | Update
|
||||
// =========================================================================
|
||||
|
||||
/**
|
||||
* @zh 更新定时器服务
|
||||
* @en Update timer service
|
||||
*
|
||||
* @param deltaTime - @zh 距上次更新的时间(毫秒)@en Time since last update in ms
|
||||
*/
|
||||
update(deltaTime: number): void;
|
||||
|
||||
/**
|
||||
* @zh 清除所有定时器和冷却
|
||||
* @en Clear all timers and cooldowns
|
||||
*/
|
||||
clear(): void;
|
||||
}
|
||||
411
packages/framework/timer/src/TimerService.ts
Normal file
411
packages/framework/timer/src/TimerService.ts
Normal file
@@ -0,0 +1,411 @@
|
||||
/**
|
||||
* @zh 定时器服务实现
|
||||
* @en Timer Service Implementation
|
||||
*
|
||||
* @zh 提供定时器调度和冷却管理的默认实现
|
||||
* @en Provides default implementation for timer scheduling and cooldown management
|
||||
*/
|
||||
|
||||
import type {
|
||||
ITimerService,
|
||||
TimerHandle,
|
||||
TimerInfo,
|
||||
TimerCallback,
|
||||
CooldownInfo
|
||||
} from './ITimerService';
|
||||
|
||||
// =============================================================================
|
||||
// 内部类型 | Internal Types
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* @zh 内部定时器数据
|
||||
* @en Internal timer data
|
||||
*/
|
||||
interface InternalTimer {
|
||||
id: string;
|
||||
callback: TimerCallback;
|
||||
remaining: number;
|
||||
repeating: boolean;
|
||||
interval: number;
|
||||
cancelled: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 内部冷却数据
|
||||
* @en Internal cooldown data
|
||||
*/
|
||||
interface InternalCooldown {
|
||||
id: string;
|
||||
duration: number;
|
||||
remaining: number;
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// 定时器句柄实现 | Timer Handle Implementation
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* @zh 定时器句柄实现
|
||||
* @en Timer handle implementation
|
||||
*/
|
||||
class TimerHandleImpl implements TimerHandle {
|
||||
private timer: InternalTimer;
|
||||
|
||||
constructor(timer: InternalTimer) {
|
||||
this.timer = timer;
|
||||
}
|
||||
|
||||
get id(): string {
|
||||
return this.timer.id;
|
||||
}
|
||||
|
||||
get isValid(): boolean {
|
||||
return !this.timer.cancelled;
|
||||
}
|
||||
|
||||
cancel(): void {
|
||||
this.timer.cancelled = true;
|
||||
}
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// 定时器服务实现 | Timer Service Implementation
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* @zh 定时器服务配置
|
||||
* @en Timer service configuration
|
||||
*/
|
||||
export interface TimerServiceConfig {
|
||||
/**
|
||||
* @zh 最大定时器数量(0 表示无限制)
|
||||
* @en Maximum number of timers (0 for unlimited)
|
||||
*/
|
||||
maxTimers?: number;
|
||||
|
||||
/**
|
||||
* @zh 最大冷却数量(0 表示无限制)
|
||||
* @en Maximum number of cooldowns (0 for unlimited)
|
||||
*/
|
||||
maxCooldowns?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 定时器服务实现
|
||||
* @en Timer service implementation
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* const timerService = new TimerService();
|
||||
*
|
||||
* // 一次性定时器 | One-time timer
|
||||
* const handle = timerService.schedule('myTimer', 1000, () => {
|
||||
* console.log('Timer fired!');
|
||||
* });
|
||||
*
|
||||
* // 重复定时器 | Repeating timer
|
||||
* timerService.scheduleRepeating('heartbeat', 100, () => {
|
||||
* console.log('Tick');
|
||||
* });
|
||||
*
|
||||
* // 冷却系统 | Cooldown system
|
||||
* timerService.startCooldown('skill_fireball', 5000);
|
||||
* if (timerService.isCooldownReady('skill_fireball')) {
|
||||
* // 可以使用技能 | Can use skill
|
||||
* }
|
||||
*
|
||||
* // 每帧更新 | Update each frame
|
||||
* timerService.update(deltaTime);
|
||||
* ```
|
||||
*/
|
||||
export class TimerService implements ITimerService {
|
||||
private timers: Map<string, InternalTimer> = new Map();
|
||||
private cooldowns: Map<string, InternalCooldown> = new Map();
|
||||
private config: Required<TimerServiceConfig>;
|
||||
|
||||
constructor(config: TimerServiceConfig = {}) {
|
||||
this.config = {
|
||||
maxTimers: config.maxTimers ?? 0,
|
||||
maxCooldowns: config.maxCooldowns ?? 0
|
||||
};
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// 定时器 API | Timer API
|
||||
// =========================================================================
|
||||
|
||||
schedule(id: string, delay: number, callback: TimerCallback): TimerHandle {
|
||||
this.cancelById(id);
|
||||
|
||||
if (this.config.maxTimers > 0 && this.timers.size >= this.config.maxTimers) {
|
||||
throw new Error(`Maximum timer limit reached: ${this.config.maxTimers}`);
|
||||
}
|
||||
|
||||
const timer: InternalTimer = {
|
||||
id,
|
||||
callback,
|
||||
remaining: Math.max(0, delay),
|
||||
repeating: false,
|
||||
interval: 0,
|
||||
cancelled: false
|
||||
};
|
||||
|
||||
this.timers.set(id, timer);
|
||||
return new TimerHandleImpl(timer);
|
||||
}
|
||||
|
||||
scheduleRepeating(
|
||||
id: string,
|
||||
interval: number,
|
||||
callback: TimerCallback,
|
||||
immediate = false
|
||||
): TimerHandle {
|
||||
this.cancelById(id);
|
||||
|
||||
if (this.config.maxTimers > 0 && this.timers.size >= this.config.maxTimers) {
|
||||
throw new Error(`Maximum timer limit reached: ${this.config.maxTimers}`);
|
||||
}
|
||||
|
||||
const safeInterval = Math.max(1, interval);
|
||||
|
||||
const timer: InternalTimer = {
|
||||
id,
|
||||
callback,
|
||||
remaining: immediate ? 0 : safeInterval,
|
||||
repeating: true,
|
||||
interval: safeInterval,
|
||||
cancelled: false
|
||||
};
|
||||
|
||||
this.timers.set(id, timer);
|
||||
return new TimerHandleImpl(timer);
|
||||
}
|
||||
|
||||
cancel(handle: TimerHandle): void {
|
||||
handle.cancel();
|
||||
this.timers.delete(handle.id);
|
||||
}
|
||||
|
||||
cancelById(id: string): void {
|
||||
const timer = this.timers.get(id);
|
||||
if (timer) {
|
||||
timer.cancelled = true;
|
||||
this.timers.delete(id);
|
||||
}
|
||||
}
|
||||
|
||||
hasTimer(id: string): boolean {
|
||||
const timer = this.timers.get(id);
|
||||
return timer !== undefined && !timer.cancelled;
|
||||
}
|
||||
|
||||
getTimerInfo(id: string): TimerInfo | null {
|
||||
const timer = this.timers.get(id);
|
||||
if (!timer || timer.cancelled) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
id: timer.id,
|
||||
remaining: timer.remaining,
|
||||
repeating: timer.repeating,
|
||||
interval: timer.repeating ? timer.interval : undefined
|
||||
};
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// 冷却 API | Cooldown API
|
||||
// =========================================================================
|
||||
|
||||
startCooldown(id: string, duration: number): void {
|
||||
if (this.config.maxCooldowns > 0 && !this.cooldowns.has(id)) {
|
||||
if (this.cooldowns.size >= this.config.maxCooldowns) {
|
||||
throw new Error(`Maximum cooldown limit reached: ${this.config.maxCooldowns}`);
|
||||
}
|
||||
}
|
||||
|
||||
const safeDuration = Math.max(0, duration);
|
||||
|
||||
this.cooldowns.set(id, {
|
||||
id,
|
||||
duration: safeDuration,
|
||||
remaining: safeDuration
|
||||
});
|
||||
}
|
||||
|
||||
isOnCooldown(id: string): boolean {
|
||||
const cooldown = this.cooldowns.get(id);
|
||||
return cooldown !== undefined && cooldown.remaining > 0;
|
||||
}
|
||||
|
||||
isCooldownReady(id: string): boolean {
|
||||
return !this.isOnCooldown(id);
|
||||
}
|
||||
|
||||
getCooldownRemaining(id: string): number {
|
||||
const cooldown = this.cooldowns.get(id);
|
||||
return cooldown ? Math.max(0, cooldown.remaining) : 0;
|
||||
}
|
||||
|
||||
getCooldownProgress(id: string): number {
|
||||
const cooldown = this.cooldowns.get(id);
|
||||
if (!cooldown || cooldown.duration <= 0) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
const elapsed = cooldown.duration - cooldown.remaining;
|
||||
return Math.min(1, Math.max(0, elapsed / cooldown.duration));
|
||||
}
|
||||
|
||||
getCooldownInfo(id: string): CooldownInfo | null {
|
||||
const cooldown = this.cooldowns.get(id);
|
||||
if (!cooldown) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const remaining = Math.max(0, cooldown.remaining);
|
||||
const progress = cooldown.duration > 0
|
||||
? Math.min(1, (cooldown.duration - remaining) / cooldown.duration)
|
||||
: 1;
|
||||
|
||||
return {
|
||||
id: cooldown.id,
|
||||
duration: cooldown.duration,
|
||||
remaining,
|
||||
progress,
|
||||
isReady: remaining <= 0
|
||||
};
|
||||
}
|
||||
|
||||
resetCooldown(id: string): void {
|
||||
this.cooldowns.delete(id);
|
||||
}
|
||||
|
||||
clearAllCooldowns(): void {
|
||||
this.cooldowns.clear();
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// 更新 | Update
|
||||
// =========================================================================
|
||||
|
||||
update(deltaTime: number): void {
|
||||
if (deltaTime <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.updateTimers(deltaTime);
|
||||
this.updateCooldowns(deltaTime);
|
||||
}
|
||||
|
||||
private updateTimers(deltaTime: number): void {
|
||||
const toRemove: string[] = [];
|
||||
|
||||
for (const [id, timer] of this.timers) {
|
||||
if (timer.cancelled) {
|
||||
toRemove.push(id);
|
||||
continue;
|
||||
}
|
||||
|
||||
timer.remaining -= deltaTime;
|
||||
|
||||
if (timer.remaining <= 0) {
|
||||
try {
|
||||
timer.callback();
|
||||
} catch (error) {
|
||||
console.error(`Timer callback error [${id}]:`, error);
|
||||
}
|
||||
|
||||
if (timer.repeating && !timer.cancelled) {
|
||||
timer.remaining += timer.interval;
|
||||
if (timer.remaining < 0) {
|
||||
timer.remaining = timer.interval;
|
||||
}
|
||||
} else {
|
||||
timer.cancelled = true;
|
||||
toRemove.push(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const id of toRemove) {
|
||||
this.timers.delete(id);
|
||||
}
|
||||
}
|
||||
|
||||
private updateCooldowns(deltaTime: number): void {
|
||||
const toRemove: string[] = [];
|
||||
|
||||
for (const [id, cooldown] of this.cooldowns) {
|
||||
cooldown.remaining -= deltaTime;
|
||||
|
||||
if (cooldown.remaining <= 0) {
|
||||
toRemove.push(id);
|
||||
}
|
||||
}
|
||||
|
||||
for (const id of toRemove) {
|
||||
this.cooldowns.delete(id);
|
||||
}
|
||||
}
|
||||
|
||||
clear(): void {
|
||||
for (const timer of this.timers.values()) {
|
||||
timer.cancelled = true;
|
||||
}
|
||||
this.timers.clear();
|
||||
this.cooldowns.clear();
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// 调试 | Debug
|
||||
// =========================================================================
|
||||
|
||||
/**
|
||||
* @zh 获取活跃定时器数量
|
||||
* @en Get active timer count
|
||||
*/
|
||||
get activeTimerCount(): number {
|
||||
return this.timers.size;
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 获取活跃冷却数量
|
||||
* @en Get active cooldown count
|
||||
*/
|
||||
get activeCooldownCount(): number {
|
||||
return this.cooldowns.size;
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 获取所有活跃定时器 ID
|
||||
* @en Get all active timer IDs
|
||||
*/
|
||||
getActiveTimerIds(): string[] {
|
||||
return Array.from(this.timers.keys());
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 获取所有活跃冷却 ID
|
||||
* @en Get all active cooldown IDs
|
||||
*/
|
||||
getActiveCooldownIds(): string[] {
|
||||
return Array.from(this.cooldowns.keys());
|
||||
}
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// 工厂函数 | Factory Functions
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* @zh 创建定时器服务
|
||||
* @en Create timer service
|
||||
*
|
||||
* @param config - @zh 配置选项 @en Configuration options
|
||||
* @returns @zh 定时器服务实例 @en Timer service instance
|
||||
*/
|
||||
export function createTimerService(config?: TimerServiceConfig): ITimerService {
|
||||
return new TimerService(config);
|
||||
}
|
||||
60
packages/framework/timer/src/index.ts
Normal file
60
packages/framework/timer/src/index.ts
Normal file
@@ -0,0 +1,60 @@
|
||||
/**
|
||||
* @zh @esengine/timer - 定时器和冷却系统
|
||||
* @en @esengine/timer - Timer and Cooldown System
|
||||
*
|
||||
* @zh 提供定时器调度和冷却管理功能
|
||||
* @en Provides timer scheduling and cooldown management
|
||||
*/
|
||||
|
||||
// =============================================================================
|
||||
// 接口和类型 | Interfaces and Types
|
||||
// =============================================================================
|
||||
|
||||
export type {
|
||||
TimerHandle,
|
||||
TimerInfo,
|
||||
CooldownInfo,
|
||||
TimerCallback,
|
||||
TimerCallbackWithTime,
|
||||
ITimerService
|
||||
} from './ITimerService';
|
||||
|
||||
// =============================================================================
|
||||
// 实现 | Implementations
|
||||
// =============================================================================
|
||||
|
||||
export type { TimerServiceConfig } from './TimerService';
|
||||
export { TimerService, createTimerService } from './TimerService';
|
||||
|
||||
// =============================================================================
|
||||
// 服务令牌 | Service Tokens
|
||||
// =============================================================================
|
||||
|
||||
export { TimerServiceToken } from './tokens';
|
||||
|
||||
// =============================================================================
|
||||
// 蓝图节点 | Blueprint Nodes
|
||||
// =============================================================================
|
||||
|
||||
export {
|
||||
// Templates
|
||||
StartCooldownTemplate,
|
||||
IsCooldownReadyTemplate,
|
||||
GetCooldownProgressTemplate,
|
||||
ResetCooldownTemplate,
|
||||
GetCooldownInfoTemplate,
|
||||
HasTimerTemplate,
|
||||
CancelTimerTemplate,
|
||||
GetTimerRemainingTemplate,
|
||||
// Executors
|
||||
StartCooldownExecutor,
|
||||
IsCooldownReadyExecutor,
|
||||
GetCooldownProgressExecutor,
|
||||
ResetCooldownExecutor,
|
||||
GetCooldownInfoExecutor,
|
||||
HasTimerExecutor,
|
||||
CancelTimerExecutor,
|
||||
GetTimerRemainingExecutor,
|
||||
// Collection
|
||||
TimerNodeDefinitions
|
||||
} from './nodes';
|
||||
543
packages/framework/timer/src/nodes/TimerNodes.ts
Normal file
543
packages/framework/timer/src/nodes/TimerNodes.ts
Normal file
@@ -0,0 +1,543 @@
|
||||
/**
|
||||
* @zh 定时器蓝图节点
|
||||
* @en Timer Blueprint Nodes
|
||||
*
|
||||
* @zh 提供定时器和冷却功能的蓝图节点
|
||||
* @en Provides blueprint nodes for timer and cooldown functionality
|
||||
*/
|
||||
|
||||
import type { BlueprintNodeTemplate, BlueprintNode, INodeExecutor, ExecutionResult } from '@esengine/blueprint';
|
||||
import type { ITimerService } from '../ITimerService';
|
||||
|
||||
// =============================================================================
|
||||
// 执行上下文接口 | Execution Context Interface
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* @zh 定时器上下文
|
||||
* @en Timer context
|
||||
*/
|
||||
interface TimerContext {
|
||||
timerService: ITimerService;
|
||||
evaluateInput(nodeId: string, pinName: string, defaultValue?: unknown): unknown;
|
||||
setOutputs(nodeId: string, outputs: Record<string, unknown>): void;
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// StartCooldown 节点 | StartCooldown Node
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* @zh StartCooldown 节点模板
|
||||
* @en StartCooldown node template
|
||||
*/
|
||||
export const StartCooldownTemplate: BlueprintNodeTemplate = {
|
||||
type: 'StartCooldown',
|
||||
title: 'Start Cooldown',
|
||||
category: 'time',
|
||||
description: 'Start a cooldown timer / 开始冷却计时',
|
||||
keywords: ['timer', 'cooldown', 'start', 'delay'],
|
||||
menuPath: ['Timer', 'Start Cooldown'],
|
||||
isPure: false,
|
||||
inputs: [
|
||||
{
|
||||
name: 'exec',
|
||||
displayName: '',
|
||||
type: 'exec'
|
||||
},
|
||||
{
|
||||
name: 'id',
|
||||
displayName: 'Cooldown ID',
|
||||
type: 'string',
|
||||
defaultValue: ''
|
||||
},
|
||||
{
|
||||
name: 'duration',
|
||||
displayName: 'Duration (ms)',
|
||||
type: 'float',
|
||||
defaultValue: 1000
|
||||
}
|
||||
],
|
||||
outputs: [
|
||||
{
|
||||
name: 'exec',
|
||||
displayName: '',
|
||||
type: 'exec'
|
||||
}
|
||||
],
|
||||
color: '#00bcd4'
|
||||
};
|
||||
|
||||
/**
|
||||
* @zh StartCooldown 节点执行器
|
||||
* @en StartCooldown node executor
|
||||
*/
|
||||
export class StartCooldownExecutor implements INodeExecutor {
|
||||
execute(node: BlueprintNode, context: unknown): ExecutionResult {
|
||||
const ctx = context as TimerContext;
|
||||
const id = ctx.evaluateInput(node.id, 'id', '') as string;
|
||||
const duration = ctx.evaluateInput(node.id, 'duration', 1000) as number;
|
||||
|
||||
if (id && ctx.timerService) {
|
||||
ctx.timerService.startCooldown(id, duration);
|
||||
}
|
||||
|
||||
return {
|
||||
outputs: {},
|
||||
nextExec: 'exec'
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// IsCooldownReady 节点 | IsCooldownReady Node
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* @zh IsCooldownReady 节点模板
|
||||
* @en IsCooldownReady node template
|
||||
*/
|
||||
export const IsCooldownReadyTemplate: BlueprintNodeTemplate = {
|
||||
type: 'IsCooldownReady',
|
||||
title: 'Is Cooldown Ready',
|
||||
category: 'time',
|
||||
description: 'Check if cooldown is ready / 检查冷却是否就绪',
|
||||
keywords: ['timer', 'cooldown', 'ready', 'check'],
|
||||
menuPath: ['Timer', 'Is Cooldown Ready'],
|
||||
isPure: true,
|
||||
inputs: [
|
||||
{
|
||||
name: 'id',
|
||||
displayName: 'Cooldown ID',
|
||||
type: 'string',
|
||||
defaultValue: ''
|
||||
}
|
||||
],
|
||||
outputs: [
|
||||
{
|
||||
name: 'isReady',
|
||||
displayName: 'Is Ready',
|
||||
type: 'bool'
|
||||
},
|
||||
{
|
||||
name: 'isOnCooldown',
|
||||
displayName: 'Is On Cooldown',
|
||||
type: 'bool'
|
||||
}
|
||||
],
|
||||
color: '#00bcd4'
|
||||
};
|
||||
|
||||
/**
|
||||
* @zh IsCooldownReady 节点执行器
|
||||
* @en IsCooldownReady node executor
|
||||
*/
|
||||
export class IsCooldownReadyExecutor implements INodeExecutor {
|
||||
execute(node: BlueprintNode, context: unknown): ExecutionResult {
|
||||
const ctx = context as TimerContext;
|
||||
const id = ctx.evaluateInput(node.id, 'id', '') as string;
|
||||
|
||||
const isReady = id ? ctx.timerService?.isCooldownReady(id) ?? true : true;
|
||||
const isOnCooldown = !isReady;
|
||||
|
||||
return {
|
||||
outputs: {
|
||||
isReady,
|
||||
isOnCooldown
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// GetCooldownProgress 节点 | GetCooldownProgress Node
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* @zh GetCooldownProgress 节点模板
|
||||
* @en GetCooldownProgress node template
|
||||
*/
|
||||
export const GetCooldownProgressTemplate: BlueprintNodeTemplate = {
|
||||
type: 'GetCooldownProgress',
|
||||
title: 'Get Cooldown Progress',
|
||||
category: 'time',
|
||||
description: 'Get cooldown progress (0-1) / 获取冷却进度 (0-1)',
|
||||
keywords: ['timer', 'cooldown', 'progress', 'remaining'],
|
||||
menuPath: ['Timer', 'Get Cooldown Progress'],
|
||||
isPure: true,
|
||||
inputs: [
|
||||
{
|
||||
name: 'id',
|
||||
displayName: 'Cooldown ID',
|
||||
type: 'string',
|
||||
defaultValue: ''
|
||||
}
|
||||
],
|
||||
outputs: [
|
||||
{
|
||||
name: 'progress',
|
||||
displayName: 'Progress',
|
||||
type: 'float'
|
||||
},
|
||||
{
|
||||
name: 'remaining',
|
||||
displayName: 'Remaining (ms)',
|
||||
type: 'float'
|
||||
},
|
||||
{
|
||||
name: 'isReady',
|
||||
displayName: 'Is Ready',
|
||||
type: 'bool'
|
||||
}
|
||||
],
|
||||
color: '#00bcd4'
|
||||
};
|
||||
|
||||
/**
|
||||
* @zh GetCooldownProgress 节点执行器
|
||||
* @en GetCooldownProgress node executor
|
||||
*/
|
||||
export class GetCooldownProgressExecutor implements INodeExecutor {
|
||||
execute(node: BlueprintNode, context: unknown): ExecutionResult {
|
||||
const ctx = context as TimerContext;
|
||||
const id = ctx.evaluateInput(node.id, 'id', '') as string;
|
||||
|
||||
const progress = id ? ctx.timerService?.getCooldownProgress(id) ?? 1 : 1;
|
||||
const remaining = id ? ctx.timerService?.getCooldownRemaining(id) ?? 0 : 0;
|
||||
const isReady = remaining <= 0;
|
||||
|
||||
return {
|
||||
outputs: {
|
||||
progress,
|
||||
remaining,
|
||||
isReady
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// ResetCooldown 节点 | ResetCooldown Node
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* @zh ResetCooldown 节点模板
|
||||
* @en ResetCooldown node template
|
||||
*/
|
||||
export const ResetCooldownTemplate: BlueprintNodeTemplate = {
|
||||
type: 'ResetCooldown',
|
||||
title: 'Reset Cooldown',
|
||||
category: 'time',
|
||||
description: 'Reset a cooldown (make it ready) / 重置冷却(使其就绪)',
|
||||
keywords: ['timer', 'cooldown', 'reset', 'clear'],
|
||||
menuPath: ['Timer', 'Reset Cooldown'],
|
||||
isPure: false,
|
||||
inputs: [
|
||||
{
|
||||
name: 'exec',
|
||||
displayName: '',
|
||||
type: 'exec'
|
||||
},
|
||||
{
|
||||
name: 'id',
|
||||
displayName: 'Cooldown ID',
|
||||
type: 'string',
|
||||
defaultValue: ''
|
||||
}
|
||||
],
|
||||
outputs: [
|
||||
{
|
||||
name: 'exec',
|
||||
displayName: '',
|
||||
type: 'exec'
|
||||
}
|
||||
],
|
||||
color: '#00bcd4'
|
||||
};
|
||||
|
||||
/**
|
||||
* @zh ResetCooldown 节点执行器
|
||||
* @en ResetCooldown node executor
|
||||
*/
|
||||
export class ResetCooldownExecutor implements INodeExecutor {
|
||||
execute(node: BlueprintNode, context: unknown): ExecutionResult {
|
||||
const ctx = context as TimerContext;
|
||||
const id = ctx.evaluateInput(node.id, 'id', '') as string;
|
||||
|
||||
if (id && ctx.timerService) {
|
||||
ctx.timerService.resetCooldown(id);
|
||||
}
|
||||
|
||||
return {
|
||||
outputs: {},
|
||||
nextExec: 'exec'
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// GetCooldownInfo 节点 | GetCooldownInfo Node
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* @zh GetCooldownInfo 节点模板
|
||||
* @en GetCooldownInfo node template
|
||||
*/
|
||||
export const GetCooldownInfoTemplate: BlueprintNodeTemplate = {
|
||||
type: 'GetCooldownInfo',
|
||||
title: 'Get Cooldown Info',
|
||||
category: 'time',
|
||||
description: 'Get detailed cooldown information / 获取详细冷却信息',
|
||||
keywords: ['timer', 'cooldown', 'info', 'details'],
|
||||
menuPath: ['Timer', 'Get Cooldown Info'],
|
||||
isPure: true,
|
||||
inputs: [
|
||||
{
|
||||
name: 'id',
|
||||
displayName: 'Cooldown ID',
|
||||
type: 'string',
|
||||
defaultValue: ''
|
||||
}
|
||||
],
|
||||
outputs: [
|
||||
{
|
||||
name: 'exists',
|
||||
displayName: 'Exists',
|
||||
type: 'bool'
|
||||
},
|
||||
{
|
||||
name: 'duration',
|
||||
displayName: 'Duration (ms)',
|
||||
type: 'float'
|
||||
},
|
||||
{
|
||||
name: 'remaining',
|
||||
displayName: 'Remaining (ms)',
|
||||
type: 'float'
|
||||
},
|
||||
{
|
||||
name: 'progress',
|
||||
displayName: 'Progress',
|
||||
type: 'float'
|
||||
},
|
||||
{
|
||||
name: 'isReady',
|
||||
displayName: 'Is Ready',
|
||||
type: 'bool'
|
||||
}
|
||||
],
|
||||
color: '#00bcd4'
|
||||
};
|
||||
|
||||
/**
|
||||
* @zh GetCooldownInfo 节点执行器
|
||||
* @en GetCooldownInfo node executor
|
||||
*/
|
||||
export class GetCooldownInfoExecutor implements INodeExecutor {
|
||||
execute(node: BlueprintNode, context: unknown): ExecutionResult {
|
||||
const ctx = context as TimerContext;
|
||||
const id = ctx.evaluateInput(node.id, 'id', '') as string;
|
||||
|
||||
const info = id ? ctx.timerService?.getCooldownInfo(id) : null;
|
||||
|
||||
return {
|
||||
outputs: {
|
||||
exists: info !== null,
|
||||
duration: info?.duration ?? 0,
|
||||
remaining: info?.remaining ?? 0,
|
||||
progress: info?.progress ?? 1,
|
||||
isReady: info?.isReady ?? true
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// HasTimer 节点 | HasTimer Node
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* @zh HasTimer 节点模板
|
||||
* @en HasTimer node template
|
||||
*/
|
||||
export const HasTimerTemplate: BlueprintNodeTemplate = {
|
||||
type: 'HasTimer',
|
||||
title: 'Has Timer',
|
||||
category: 'time',
|
||||
description: 'Check if a timer exists / 检查定时器是否存在',
|
||||
keywords: ['timer', 'exists', 'check', 'has'],
|
||||
menuPath: ['Timer', 'Has Timer'],
|
||||
isPure: true,
|
||||
inputs: [
|
||||
{
|
||||
name: 'id',
|
||||
displayName: 'Timer ID',
|
||||
type: 'string',
|
||||
defaultValue: ''
|
||||
}
|
||||
],
|
||||
outputs: [
|
||||
{
|
||||
name: 'exists',
|
||||
displayName: 'Exists',
|
||||
type: 'bool'
|
||||
}
|
||||
],
|
||||
color: '#00bcd4'
|
||||
};
|
||||
|
||||
/**
|
||||
* @zh HasTimer 节点执行器
|
||||
* @en HasTimer node executor
|
||||
*/
|
||||
export class HasTimerExecutor implements INodeExecutor {
|
||||
execute(node: BlueprintNode, context: unknown): ExecutionResult {
|
||||
const ctx = context as TimerContext;
|
||||
const id = ctx.evaluateInput(node.id, 'id', '') as string;
|
||||
|
||||
const exists = id ? ctx.timerService?.hasTimer(id) ?? false : false;
|
||||
|
||||
return {
|
||||
outputs: {
|
||||
exists
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// CancelTimer 节点 | CancelTimer Node
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* @zh CancelTimer 节点模板
|
||||
* @en CancelTimer node template
|
||||
*/
|
||||
export const CancelTimerTemplate: BlueprintNodeTemplate = {
|
||||
type: 'CancelTimer',
|
||||
title: 'Cancel Timer',
|
||||
category: 'time',
|
||||
description: 'Cancel a timer by ID / 通过 ID 取消定时器',
|
||||
keywords: ['timer', 'cancel', 'stop', 'clear'],
|
||||
menuPath: ['Timer', 'Cancel Timer'],
|
||||
isPure: false,
|
||||
inputs: [
|
||||
{
|
||||
name: 'exec',
|
||||
displayName: '',
|
||||
type: 'exec'
|
||||
},
|
||||
{
|
||||
name: 'id',
|
||||
displayName: 'Timer ID',
|
||||
type: 'string',
|
||||
defaultValue: ''
|
||||
}
|
||||
],
|
||||
outputs: [
|
||||
{
|
||||
name: 'exec',
|
||||
displayName: '',
|
||||
type: 'exec'
|
||||
}
|
||||
],
|
||||
color: '#00bcd4'
|
||||
};
|
||||
|
||||
/**
|
||||
* @zh CancelTimer 节点执行器
|
||||
* @en CancelTimer node executor
|
||||
*/
|
||||
export class CancelTimerExecutor implements INodeExecutor {
|
||||
execute(node: BlueprintNode, context: unknown): ExecutionResult {
|
||||
const ctx = context as TimerContext;
|
||||
const id = ctx.evaluateInput(node.id, 'id', '') as string;
|
||||
|
||||
if (id && ctx.timerService) {
|
||||
ctx.timerService.cancelById(id);
|
||||
}
|
||||
|
||||
return {
|
||||
outputs: {},
|
||||
nextExec: 'exec'
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// GetTimerRemaining 节点 | GetTimerRemaining Node
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* @zh GetTimerRemaining 节点模板
|
||||
* @en GetTimerRemaining node template
|
||||
*/
|
||||
export const GetTimerRemainingTemplate: BlueprintNodeTemplate = {
|
||||
type: 'GetTimerRemaining',
|
||||
title: 'Get Timer Remaining',
|
||||
category: 'time',
|
||||
description: 'Get remaining time for a timer / 获取定时器剩余时间',
|
||||
keywords: ['timer', 'remaining', 'time', 'left'],
|
||||
menuPath: ['Timer', 'Get Timer Remaining'],
|
||||
isPure: true,
|
||||
inputs: [
|
||||
{
|
||||
name: 'id',
|
||||
displayName: 'Timer ID',
|
||||
type: 'string',
|
||||
defaultValue: ''
|
||||
}
|
||||
],
|
||||
outputs: [
|
||||
{
|
||||
name: 'remaining',
|
||||
displayName: 'Remaining (ms)',
|
||||
type: 'float'
|
||||
},
|
||||
{
|
||||
name: 'exists',
|
||||
displayName: 'Exists',
|
||||
type: 'bool'
|
||||
}
|
||||
],
|
||||
color: '#00bcd4'
|
||||
};
|
||||
|
||||
/**
|
||||
* @zh GetTimerRemaining 节点执行器
|
||||
* @en GetTimerRemaining node executor
|
||||
*/
|
||||
export class GetTimerRemainingExecutor implements INodeExecutor {
|
||||
execute(node: BlueprintNode, context: unknown): ExecutionResult {
|
||||
const ctx = context as TimerContext;
|
||||
const id = ctx.evaluateInput(node.id, 'id', '') as string;
|
||||
|
||||
const info = id ? ctx.timerService?.getTimerInfo(id) : null;
|
||||
|
||||
return {
|
||||
outputs: {
|
||||
remaining: info?.remaining ?? 0,
|
||||
exists: info !== null
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// 节点定义集合 | Node Definition Collection
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* @zh 定时器节点定义
|
||||
* @en Timer node definitions
|
||||
*/
|
||||
export const TimerNodeDefinitions = [
|
||||
{ template: StartCooldownTemplate, executor: new StartCooldownExecutor() },
|
||||
{ template: IsCooldownReadyTemplate, executor: new IsCooldownReadyExecutor() },
|
||||
{ template: GetCooldownProgressTemplate, executor: new GetCooldownProgressExecutor() },
|
||||
{ template: ResetCooldownTemplate, executor: new ResetCooldownExecutor() },
|
||||
{ template: GetCooldownInfoTemplate, executor: new GetCooldownInfoExecutor() },
|
||||
{ template: HasTimerTemplate, executor: new HasTimerExecutor() },
|
||||
{ template: CancelTimerTemplate, executor: new CancelTimerExecutor() },
|
||||
{ template: GetTimerRemainingTemplate, executor: new GetTimerRemainingExecutor() }
|
||||
];
|
||||
27
packages/framework/timer/src/nodes/index.ts
Normal file
27
packages/framework/timer/src/nodes/index.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
/**
|
||||
* @zh 定时器蓝图节点导出
|
||||
* @en Timer Blueprint Nodes Export
|
||||
*/
|
||||
|
||||
export {
|
||||
// Templates
|
||||
StartCooldownTemplate,
|
||||
IsCooldownReadyTemplate,
|
||||
GetCooldownProgressTemplate,
|
||||
ResetCooldownTemplate,
|
||||
GetCooldownInfoTemplate,
|
||||
HasTimerTemplate,
|
||||
CancelTimerTemplate,
|
||||
GetTimerRemainingTemplate,
|
||||
// Executors
|
||||
StartCooldownExecutor,
|
||||
IsCooldownReadyExecutor,
|
||||
GetCooldownProgressExecutor,
|
||||
ResetCooldownExecutor,
|
||||
GetCooldownInfoExecutor,
|
||||
HasTimerExecutor,
|
||||
CancelTimerExecutor,
|
||||
GetTimerRemainingExecutor,
|
||||
// Collection
|
||||
TimerNodeDefinitions
|
||||
} from './TimerNodes';
|
||||
16
packages/framework/timer/src/tokens.ts
Normal file
16
packages/framework/timer/src/tokens.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
/**
|
||||
* @zh 定时器服务令牌
|
||||
* @en Timer Service Tokens
|
||||
*/
|
||||
|
||||
import { createServiceToken } from '@esengine/ecs-framework';
|
||||
import type { ITimerService } from './ITimerService';
|
||||
|
||||
/**
|
||||
* @zh 定时器服务令牌
|
||||
* @en Timer service token
|
||||
*
|
||||
* @zh 用于注入定时器服务
|
||||
* @en Used for injecting timer service
|
||||
*/
|
||||
export const TimerServiceToken = createServiceToken<ITimerService>('timerService');
|
||||
22
packages/framework/timer/tsconfig.build.json
Normal file
22
packages/framework/timer/tsconfig.build.json
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2020",
|
||||
"module": "ES2020",
|
||||
"moduleResolution": "bundler",
|
||||
"lib": ["ES2020", "DOM"],
|
||||
"outDir": "./dist",
|
||||
"rootDir": "./src",
|
||||
"strict": true,
|
||||
"esModuleInterop": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"skipLibCheck": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"declaration": true,
|
||||
"declarationMap": true,
|
||||
"resolveJsonModule": true,
|
||||
"experimentalDecorators": true,
|
||||
"emitDecoratorMetadata": true
|
||||
},
|
||||
"include": ["src/**/*"],
|
||||
"exclude": ["node_modules", "dist"]
|
||||
}
|
||||
23
packages/framework/timer/tsconfig.json
Normal file
23
packages/framework/timer/tsconfig.json
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"extends": "../../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"outDir": "./dist",
|
||||
"rootDir": "./src"
|
||||
},
|
||||
"include": [
|
||||
"src/**/*"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
"dist"
|
||||
],
|
||||
"references": [
|
||||
{
|
||||
"path": "../../framework/core"
|
||||
},
|
||||
{
|
||||
"path": "../../framework/blueprint"
|
||||
}
|
||||
]
|
||||
}
|
||||
14
packages/framework/timer/tsup.config.ts
Normal file
14
packages/framework/timer/tsup.config.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { defineConfig } from 'tsup';
|
||||
|
||||
export default defineConfig({
|
||||
entry: ['src/index.ts'],
|
||||
format: ['esm'],
|
||||
dts: true,
|
||||
sourcemap: true,
|
||||
clean: true,
|
||||
external: [
|
||||
'@esengine/ecs-framework',
|
||||
'@esengine/blueprint'
|
||||
],
|
||||
tsconfig: 'tsconfig.build.json'
|
||||
});
|
||||
Reference in New Issue
Block a user