feat(framework): RoomRPCBus 把 NetClient 'message' 分发到 room handlers

端到端打通「收包 → 路由分界 → RoomStore 更新」」」」」」」」」」」

RoomRPCBus.start() 订阅 NetClient.bus 'message':
- route==='room' 的包 → 查 roomHandlers[msg.rpc](ctx, msg.data)
- 其他 route 不处理(归 Router 单独管平台/agent)

未注册 room rpc 显式抛错(第二准则:不静默吞包)
start/stop 幂等,可重复调用

业务侧在 PlatformSession 构造后:
  const roomBus = new RoomRPCBus(net.bus, session);
  roomBus.start();  // 进房前
  roomBus.stop();   // 退房后

162/162 tests pass, typecheck exit 0。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-31 21:00:02 +08:00
co-authored by Claude Opus 5
parent 705c0389b5
commit 64b818d514
2 changed files with 121 additions and 0 deletions
@@ -0,0 +1,50 @@
import type { EventBus } from '../core/events.ts';
import type { NetClientEvents } from '../net/net-client.ts';
import type { InboundMessage } from '../core/types/envelope.ts';
import { roomHandlers } from '../protocol/room-handlers.ts';
import type { PlatformSession } from './session.ts';
/**
* RoomRPCBus:把 NetClient 的 'message' 事件按 route 分发到 room handlers。
*
* 监听 NetClient 'message' → 过滤 route==='room' → 查 roomHandlers[rpc](ctx, data)。
*
* 业务侧在 PlatformSession 构造后立即创建 RoomRPCBus 并 start(),
* 后续 stop() 退订(进/退房切换清理)。
*/
export class RoomRPCBus {
private handler: ((msg: InboundMessage) => void) | null = null;
constructor(
private readonly bus: EventBus<NetClientEvents>,
private readonly session: PlatformSession,
) {}
/** 开始监听 NetClient 消息事件 */
start(): void {
if (this.handler) return; // 幂等
this.handler = (msg) => this.dispatch(msg);
this.bus.on('message', this.handler);
}
/** 停止监听(切房/退出时调用) */
stop(): void {
if (this.handler) { this.bus.off('message', this.handler); this.handler = null; }
}
private dispatch(msg: InboundMessage): void {
if (msg.route !== 'room') return; // 平台三层 + 对局 route 由 Router 单独处理
const handler = (roomHandlers as unknown as Record<string, ((ctx: any, data: any) => void) | undefined>)[msg.rpc];
if (!handler) {
// 未注册 rpc:roomHandlers 的 Object.freeze 已确保此处为 undefined
// 不静默吞包:抛错便于真机端到端发现协议漂移
throw new Error(
`RoomRPCBus: 未注册的 room rpc "${msg.rpc}"(roomHandlers 不覆盖)`,
);
}
handler(
{ room: this.session.room, player: this.session.player },
msg.data as Record<string, unknown>,
);
}
}