Colyseus 跨房间通信实现
在 Colyseus 中,跨房间通信是一个常见的需求,尤其是当需要在不同的房间之间共享信息或广播事件时。以下是几种实现跨房间通信的方法:
1. 使用 Colyseus 的 Presence
服务
Presence
是 Colyseus 提供的分布式状态管理工具,允许不同房间之间共享状态和消息。
示例实现
服务端代码:
import { Room, Client } from "colyseus";
import { Presence } from "@colyseus/core";
export class RoomA extends Room {
async onCreate(options: any) {
// 在房间 A 中设置监听器
this.presence.subscribe("globalMessage", (message) => {
console.log(\`房间 A 收到消息: \${message}\`);
});
}
onMessage(client: Client, message: any) {
if (message.type === "broadcast") {
// 将消息广播到 Presence
this.presence.publish("globalMessage", message.data);
}
}
}
export class RoomB extends Room {
async onCreate(options: any) {
// 在房间 B 中设置监听器
this.presence.subscribe("globalMessage", (message) => {
console.log(\`房间 B 收到消息: \${message}\`);
});
}
}
功能说明
- 使用
this.presence.publish
将消息广播到Presence
中。 - 使用
this.presence.subscribe
在房间内监听消息。
优点:
- 简单易用,支持分布式。
- 不需要直接管理房间间的引用。
缺点:
- 如果消息量大,可能会影响性能。
2. 使用 Room
实例之间的直接引用
在服务器中,Colyseus 允许保存和管理房间实例,因此可以通过直接引用房间实例来实现跨房间通信。
示例实现
服务端代码:
import { Room, Client } from "colyseus";
const rooms: { [key: string]: Room } = {}; // 存储所有房间实例
export class RoomA extends Room {
async onCreate(options: any) {
rooms["RoomA"] = this; // 将当前房间实例保存
}
onMessage(client: Client, message: any) {
if (message.type === "sendToRoomB") {
// 发送消息到 RoomB
const roomB = rooms["RoomB"];
if (roomB) {
roomB.broadcast("messageFromA", message.data);
}
}
}
}
export class RoomB extends Room {
async onCreate(options: any) {
rooms["RoomB"] = this; // 将当前房间实例保存
}
onMessage(client: Client, message: any) {
if (message.type === "sendToRoomA") {
// 发送消息到 RoomA
const roomA = rooms["RoomA"];
if (roomA) {
roomA.broadcast("messageFromB", message.data);
}
}
}
}
功能说明
- 使用全局对象存储房间实例。
- 通过实例直接调用目标房间的方法。
优点:
- 灵活性高,可以直接操作目标房间。
- 不依赖分布式系统。
缺点:
- 在分布式服务器中需要额外同步房间实例。
- 适合单服务器场景。
3. 使用 Redis 或其他消息队列
对于分布式部署,建议使用 Redis 等消息队列系统来实现跨房间通信。
示例实现
安装 Redis 和相关依赖:
npm install ioredis
服务端代码:
import { Room, Client } from "colyseus";
import Redis from "ioredis";
const redis = new Redis();
export class RoomA extends Room {
async onCreate(options: any) {
// 监听来自 Redis 的消息
redis.subscribe("globalChannel");
redis.on("message", (channel, message) => {
if (channel === "globalChannel") {
this.broadcast("messageFromRedis", JSON.parse(message));
}
});
}
onMessage(client: Client, message: any) {
if (message.type === "broadcastToAll") {
// 发布消息到 Redis
redis.publish("globalChannel", JSON.stringify(message.data));
}
}
}
export class RoomB extends Room {
async onCreate(options: any) {
// 监听来自 Redis 的消息
redis.subscribe("globalChannel");
redis.on("message", (channel, message) => {
if (channel === "globalChannel") {
this.broadcast("messageFromRedis", JSON.parse(message));
}
});
}
}
功能说明
- 使用 Redis 作为消息队列,实现不同房间之间的消息广播。
redis.publish
用于发送消息,redis.subscribe
用于接收消息。
优点:
- 支持大规模分布式部署。
- 可靠性高。
缺点:
- 增加了外部依赖。
4. 使用全局事件总线
如果不需要分布式,可以使用 Node.js 的事件系统或其他事件库(如 EventEmitter
)实现跨房间通信。
示例实现
服务端代码:
import { Room, Client } from "colyseus";
import { EventEmitter } from "events";
const eventBus = new EventEmitter();
export class RoomA extends Room {
async onCreate(options: any) {
// 监听全局事件
eventBus.on("globalMessage", (message) => {
this.broadcast("messageFromBus", message);
});
}
onMessage(client: Client, message: any) {
if (message.type === "broadcast") {
// 触发全局事件
eventBus.emit("globalMessage", message.data);
}
}
}
export class RoomB extends Room {
async onCreate(options: any) {
// 监听全局事件
eventBus.on("globalMessage", (message) => {
this.broadcast("messageFromBus", message);
});
}
}
功能说明
- 使用
EventEmitter
作为全局事件总线。 - 不依赖外部服务。
优点:
- 实现简单,适合单服务器场景。
- 无外部依赖。
缺点:
- 不适用于分布式环境。
总结
- 单服务器:推荐使用
Presence
或EventEmitter
。 - 分布式服务器:推荐使用
Presence
或 Redis 等消息队列。 - 如果需要更灵活的控制,可以结合房间实例和消息队列的方式。
每种方法都有其适用场景和权衡,选择时应根据系统架构和性能需求决定。