Files
esengine/packages/editor-core/src/Services/MessageHub.ts
YHH 3fb6f919f8 Feature/tilemap editor (#237)
* feat: 添加 Tilemap 编辑器插件和组件生命周期支持

* feat(editor-core): 添加声明式插件注册 API

* feat(editor-core): 改进tiledmap结构合并tileset进tiledmapeditor

* feat: 添加 editor-runtime SDK 和插件系统改进

* fix(ci): 修复SceneResourceManager里变量未使用问题
2025-11-25 22:23:19 +08:00

222 lines
5.9 KiB
TypeScript

import type { IService } from '@esengine/ecs-framework';
import { Injectable } from '@esengine/ecs-framework';
import { createLogger } from '@esengine/ecs-framework';
const logger = createLogger('MessageHub');
/**
* 消息处理器类型
*/
export type MessageHandler<T = any> = (data: T) => void | Promise<void>;
/**
* 消息订阅
*/
interface MessageSubscription {
topic: string;
handler: MessageHandler;
once: boolean;
}
/**
* 消息总线
*
* 提供插件间的事件通信机制,支持订阅/发布模式。
*/
@Injectable()
export class MessageHub implements IService {
private subscriptions: Map<string, MessageSubscription[]> = new Map();
private subscriptionId: number = 0;
/**
* 订阅消息
*
* @param topic - 消息主题
* @param handler - 消息处理器
* @returns 取消订阅的函数
*/
public subscribe<T = any>(topic: string, handler: MessageHandler<T>): () => void {
return this.addSubscription(topic, handler, false);
}
/**
* 订阅一次性消息
*
* @param topic - 消息主题
* @param handler - 消息处理器
* @returns 取消订阅的函数
*/
public subscribeOnce<T = any>(topic: string, handler: MessageHandler<T>): () => void {
return this.addSubscription(topic, handler, true);
}
/**
* 添加订阅
*/
private addSubscription(topic: string, handler: MessageHandler, once: boolean): () => void {
const subscription: MessageSubscription = {
topic,
handler,
once
};
let subs = this.subscriptions.get(topic);
if (!subs) {
subs = [];
this.subscriptions.set(topic, subs);
}
subs.push(subscription);
const subId = ++this.subscriptionId;
logger.debug(`Subscribed to topic: ${topic} (id: ${subId}, once: ${once})`);
return () => {
this.unsubscribe(topic, subscription);
logger.debug(`Unsubscribed from topic: ${topic} (id: ${subId})`);
};
}
/**
* 取消订阅
*/
private unsubscribe(topic: string, subscription: MessageSubscription): void {
const subs = this.subscriptions.get(topic);
if (!subs) {
return;
}
const index = subs.indexOf(subscription);
if (index !== -1) {
subs.splice(index, 1);
}
if (subs.length === 0) {
this.subscriptions.delete(topic);
}
}
/**
* 发布消息
*
* @param topic - 消息主题
* @param data - 消息数据
*/
public async publish<T = any>(topic: string, data?: T): Promise<void> {
const subs = this.subscriptions.get(topic);
if (!subs || subs.length === 0) {
logger.debug(`No subscribers for topic: ${topic}`);
return;
}
logger.debug(`Publishing message to topic: ${topic} (${subs.length} subscribers)`);
const onceSubscriptions: MessageSubscription[] = [];
for (const sub of subs) {
try {
await sub.handler(data);
if (sub.once) {
onceSubscriptions.push(sub);
}
} catch (error) {
logger.error(`Error in message handler for topic ${topic}:`, error);
}
}
for (const sub of onceSubscriptions) {
this.unsubscribe(topic, sub);
}
}
/**
* 同步发布消息
*
* @param topic - 消息主题
* @param data - 消息数据
*/
public publishSync<T = any>(topic: string, data?: T): void {
const subs = this.subscriptions.get(topic);
if (!subs || subs.length === 0) {
logger.debug(`No subscribers for topic: ${topic}`);
return;
}
logger.debug(`Publishing sync message to topic: ${topic} (${subs.length} subscribers)`);
const onceSubscriptions: MessageSubscription[] = [];
for (const sub of subs) {
try {
const result = sub.handler(data);
if (result instanceof Promise) {
logger.warn(`Async handler used with publishSync for topic: ${topic}`);
}
if (sub.once) {
onceSubscriptions.push(sub);
}
} catch (error) {
logger.error(`Error in message handler for topic ${topic}:`, error);
}
}
for (const sub of onceSubscriptions) {
this.unsubscribe(topic, sub);
}
}
/**
* 取消所有指定主题的订阅
*
* @param topic - 消息主题
*/
public unsubscribeAll(topic: string): void {
const deleted = this.subscriptions.delete(topic);
if (deleted) {
logger.debug(`Unsubscribed all from topic: ${topic}`);
}
}
/**
* 检查主题是否有订阅者
*
* @param topic - 消息主题
* @returns 是否有订阅者
*/
public hasSubscribers(topic: string): boolean {
const subs = this.subscriptions.get(topic);
return subs !== undefined && subs.length > 0;
}
/**
* 获取所有主题
*
* @returns 主题列表
*/
public getTopics(): string[] {
return Array.from(this.subscriptions.keys());
}
/**
* 获取主题的订阅者数量
*
* @param topic - 消息主题
* @returns 订阅者数量
*/
public getSubscriberCount(topic: string): number {
const subs = this.subscriptions.get(topic);
return subs ? subs.length : 0;
}
/**
* 释放资源
*/
public dispose(): void {
this.subscriptions.clear();
logger.info('MessageHub disposed');
}
}
// Service identifier for DI registration (用于跨包插件访问)
// 使用 Symbol.for 确保跨包共享同一个 Symbol
export const IMessageHub = Symbol.for('IMessageHub');