Real-time Messaging
Send a message to one user or broadcast it to a group, at global or room scope.
Messaging is fire-and-forget pub/sub: a message reaches whoever is subscribed to that channel at the moment it is published. There is no acknowledgement, no retry and no replay, so a client that is disconnected, still connecting, or not yet subscribed simply misses it. Anything a player must not miss belongs in state ($global / $room), which they read on (re)connect — use messages for the transient layer on top.
Server API
Every message carries a type string, which is what clients subscribe to.
$global.broadcastToAll(type: string, message: any)— every connected user$global.sendMessageToUser(type: string, account: string, message: any)— one user, wherever they are$room.broadcastToRoom(type: string, message: any)— every user in the caller’s room$room.sendMessageToUser(type: string, account: string, message: any)— one user in the caller’s room
Client API
server.onGlobalMessage(type, (message) => {})server.onRoomMessage(roomId, type, (message) => {})
Both return an unsubscribe function. Call them from useEffect and return it — listeners are not
deduplicated, so a subscribe in the render body registers another one on every render and each
message is then handled repeatedly. Two things the SDK does not do for you:
- Subscriptions are not restored after a reconnect. Keying the effect on
connected/rsConnected, as in the example below, re-subscribes. onRoomMessageneeds a joined room. Before the Room Server connection is up the subscribe is dropped and the callback never fires — gate it onrsConnectedand a realcurrentRoomId.
Example: Real-time Chat
export class Server {
async sendGlobalChat(message) {
await $global.broadcastToAll("GLOBAL_CHAT", {
sender: $sender.account,
text: message,
timestamp: Date.now(),
});
}
// No roomId parameter: $room already targets the caller's room.
async sendRoomChat(message) {
await $room.broadcastToRoom("ROOM_CHAT", {
sender: $sender.account,
text: message,
timestamp: Date.now(),
});
}
}