diff --git a/cocoscreator_projects/YouleNexus/assets/framework/platform/room-rpc-bus.ts b/cocoscreator_projects/YouleNexus/assets/framework/platform/room-rpc-bus.ts new file mode 100644 index 0000000..247079c --- /dev/null +++ b/cocoscreator_projects/YouleNexus/assets/framework/platform/room-rpc-bus.ts @@ -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, + 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 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, + ); + } +} \ No newline at end of file diff --git a/cocoscreator_projects/framework-tests/platform/room-rpc-bus.test.ts b/cocoscreator_projects/framework-tests/platform/room-rpc-bus.test.ts new file mode 100644 index 0000000..71af569 --- /dev/null +++ b/cocoscreator_projects/framework-tests/platform/room-rpc-bus.test.ts @@ -0,0 +1,71 @@ +import { test } from 'node:test'; +import assert from 'node:assert/strict'; +import { EventBus } from '../../YouleNexus/assets/framework/core/events.ts'; +import { PlatformSession } from '../../YouleNexus/assets/framework/platform/session.ts'; +import { RoomRPCBus } from '../../YouleNexus/assets/framework/platform/room-rpc-bus.ts'; +import type { NetClientEvents } from '../../YouleNexus/assets/framework/net/net-client.ts'; + +test('RoomRPCBus: route=room 的包分发到对应 handler', () => { + const bus = new EventBus(); + const session = new PlatformSession(bus); + const roomBus = new RoomRPCBus(bus, session); + roomBus.start(); + + // 触发 room apply_free_room + bus.emit('message', { route: 'room', rpc: 'other_apply_free_room', data: { applyer: 2 } } as any); + assert.equal(session.room.state.value.state, 1, 'apply_free_room 后 state=1'); + + roomBus.stop(); +}); + +test('RoomRPCBus: route=agent/platform 不被分发(归 Router 处理)', () => { + const bus = new EventBus(); + const session = new PlatformSession(bus); + const roomBus = new RoomRPCBus(bus, session); + roomBus.start(); + + // 故意发一个 route=room + rpc 不存在的包,不应崩 + bus.emit('message', { route: 'room', rpc: 'unknown_rpc', data: {} } as any); + // state 不应变化 + assert.equal(session.room.state.value.state, 0); + + roomBus.stop(); +}); + +test('RoomRPCBus: stop 后不再分发', () => { + const bus = new EventBus(); + const session = new PlatformSession(bus); + const roomBus = new RoomRPCBus(bus, session); + roomBus.start(); + roomBus.stop(); + + bus.emit('message', { route: 'room', rpc: 'other_apply_free_room', data: {} } as any); + // stop 后 handler 不该被调用,state 应保持 0 + assert.equal(session.room.state.value.state, 0); +}); + +test('RoomRPCBus: room rpc 走 handler,其他 route 忽略', () => { + const bus = new EventBus(); + const session = new PlatformSession(bus); + const roomBus = new RoomRPCBus(bus, session); + roomBus.start(); + + // 模拟 login 后玩家入房,players 数组已存在 + session.room.applyRecovery({ + roomcode: 'R1', seat: 1, + players: [null, { playerid: 100 } as any, null, null], + } as any); + + // 第三方加入 + bus.emit('message', { + route: 'room', rpc: 'other_join_room', + data: { seat: 3, playerid: 300, nickname: '丙' }, + } as any); + assert.equal(session.room.state.value.players[2]?.playerid, 300); + + // 第三方退出 + bus.emit('message', { route: 'room', rpc: 'other_exit_room', data: { seat: 3 } } as any); + assert.equal(session.room.state.value.players[2], null); + + roomBus.stop(); +}); \ No newline at end of file