Skip to main content

Room Callbacks

Endless rooms provide various callbacks to handle room events, player connections, and state changes. These callbacks allow you to create responsive multiplayer experiences.

Available Callbacks

Connection Callbacks

CallbackDescription
onRoomCreatedCalled when room is successfully created
onJoinedRoomCalled when successfully joined a room
onLeftRoomCalled when left a room
onConnectedToRoomCalled when fully connected to room
onDisconnectedFromRoomCalled when disconnected from room

Player Management Callbacks

CallbackDescription
onPeerJoinedCalled when a player joins the room
onPeerLeftCalled when a player leaves the room
onPeerConnectedCalled when a player connects to room
onPeerDisconnectedCalled when a player disconnects from room

Reconnection Callbacks

CallbackDescription
reconnectSuccessfulCalled when reconnection succeeds
attemptingReconnectCalled when attempting to reconnect

Examples

Complete Callback Setup

MoitribeSDK('my-game', 'createendlessroom', {
variant: 1,

// Room lifecycle callbacks
onRoomCreated: (status, room) => {
if (status) {
console.log('Room created:', room.roomID);
initializeGame(room);
showInvitationCode(room.roomID);
} else {
console.error('Failed to create room');
showCreationError();
}
},

onJoinedRoom: (status, room) => {
if (status) {
console.log('Joined room:', room.roomID);
syncWithRoomState(room);
enableGameControls();
} else {
console.error('Failed to join room');
showJoinError();
}
},

onLeftRoom: (status, roomID) => {
console.log('Left room:', roomID);
cleanupGameState();
showMainMenu();
},

onConnectedToRoom: (room) => {
console.log('Connected to room, ready to play');
startGameLoop();
announcePlayerJoin();
},

onDisconnectedFromRoom: (room) => {
console.log('Disconnected from room');
pauseGameLoop();
showReconnectDialog();
},

// Player management callbacks
onPeerJoined: (room, participantList) => {
console.log('Player joined. Total players:', participantList.length);
updatePlayerList(room.participants);
showJoinNotification(getLastJoinedPlayer(room));
},

onPeerLeft: (room, participantList) => {
console.log('Player left. Total players:', participantList.length);
updatePlayerList(room.participants);
showLeaveNotification(getLastLeftPlayer(room));
handlePlayerDisconnection(getLastLeftPlayer(room));
},

onPeerConnected: (room, participantList) => {
console.log('Player connected to room');
updateConnectionStatus(room.participants);
},

onPeerDisconnected: (room, participantList) => {
console.log('Player disconnected from room');
updateConnectionStatus(room.participants);
handlePlayerDisconnection(getDisconnectedPlayer(room));
},

// Reconnection callbacks
attemptingReconnect: () => {
console.log('Attempting to reconnect...');
showReconnectingIndicator();
},

reconnectSuccessful: () => {
console.log('Reconnection successful');
hideReconnectingIndicator();
syncWithServer();
},

// Message callback
onMessageReceived: (messageData, senderParticipantID, isReliable) => {
handleIncomingMessage(messageData, senderParticipantID, isReliable);
}
}, (result) => {
console.log('Room creation request sent:', result);
});

TypeScript Example

import MoitribeSDK from '@veniso/moitribe-js';

interface RoomCallbacks {
onRoomCreated?: (status: boolean, room: Room) => void;
onJoinedRoom?: (status: boolean, room: Room) => void;
onLeftRoom?: (status: boolean, roomID: string) => void;
onConnectedToRoom?: (room: Room) => void;
onDisconnectedFromRoom?: (room: Room) => void;
onPeerJoined?: (room: Room, participantList: string[]) => void;
onPeerLeft?: (room: Room, participantList: string[]) => void;
onPeerConnected?: (room: Room, participantList: string[]) => void;
onPeerDisconnected?: (room: Room, participantList: string[]) => void;
attemptingReconnect?: () => void;
reconnectSuccessful?: () => void;
onMessageReceived?: (messageData: ArrayBuffer, senderParticipantID: string, isReliable: boolean) => void;
}

class EndlessRoomManager {
private currentRoom: Room | null = null;
private playerStates = new Map<string, PlayerState>();

createRoom(variant: number): void {
const callbacks: RoomCallbacks = {
onRoomCreated: this.handleRoomCreated.bind(this),
onJoinedRoom: this.handleJoinedRoom.bind(this),
onLeftRoom: this.handleLeftRoom.bind(this),
onConnectedToRoom: this.handleConnectedToRoom.bind(this),
onDisconnectedFromRoom: this.handleDisconnectedFromRoom.bind(this),
onPeerJoined: this.handlePeerJoined.bind(this),
onPeerLeft: this.handlePeerLeft.bind(this),
onPeerConnected: this.handlePeerConnected.bind(this),
onPeerDisconnected: this.handlePeerDisconnected.bind(this),
attemptingReconnect: this.handleAttemptingReconnect.bind(this),
reconnectSuccessful: this.handleReconnectSuccessful.bind(this),
onMessageReceived: this.handleMessageReceived.bind(this)
};

MoitribeSDK('my-game', 'createendlessroom', {
variant,
...callbacks
});
}

private handleRoomCreated(status: boolean, room: Room): void {
if (status) {
this.currentRoom = room;
console.log(`Room created: ${room.roomID}`);
this.initializeGameState(room);
this.showRoomUI(room);
} else {
console.error('Room creation failed');
this.showCreationError();
}
}

private handleJoinedRoom(status: boolean, room: Room): void {
if (status) {
this.currentRoom = room;
console.log(`Joined room: ${room.roomID}`);
this.syncWithExistingRoom(room);
this.enableGameplay();
} else {
console.error('Failed to join room');
this.showJoinError();
}
}

private handleLeftRoom(status: boolean, roomID: string): void {
console.log(`Left room: ${roomID}`);
this.currentRoom = null;
this.cleanupGameState();
this.returnToMainMenu();
}

private handleConnectedToRoom(room: Room): void {
console.log('Fully connected to room');
this.startGameLoop();
this.broadcastPlayerJoin();
}

private handleDisconnectedFromRoom(room: Room): void {
console.log('Disconnected from room');
this.pauseGameLoop();
this.attemptReconnection();
}

private handlePeerJoined(room: Room, participantList: string[]): void {
console.log(`Player joined. Total: ${participantList.length}`);
this.updatePlayerList(room.participants);
this.showJoinNotification(this.getLastJoinedPlayer(room));
}

private handlePeerLeft(room: Room, participantList: string[]): void {
console.log(`Player left. Total: ${participantList.length}`);
this.updatePlayerList(room.participants);
this.handlePlayerDeparture(this.getLastLeftPlayer(room));
}

private handlePeerConnected(room: Room, participantList: string[]): void {
console.log('Player reconnected');
this.updateConnectionIndicators(room.participants);
}

private handlePeerDisconnected(room: Room, participantList: string[]): void {
console.log('Player disconnected');
this.updateConnectionIndicators(room.participants);
this.handlePlayerLoss(this.getDisconnectedPlayer(room));
}

private handleAttemptingReconnect(): void {
console.log('Attempting to reconnect...');
this.showReconnectingUI();
}

private handleReconnectSuccessful(): void {
console.log('Reconnection successful');
this.hideReconnectingUI();
this.resyncWithServer();
}

private handleMessageReceived(messageData: ArrayBuffer, senderParticipantID: string, isReliable: boolean): void {
this.processIncomingMessage(messageData, senderParticipantID, isReliable);
}

// Helper methods
private initializeGameState(room: Room): void {
// Initialize game state for new room
}

private syncWithExistingRoom(room: Room): void {
// Sync with existing room state
}

private updatePlayerList(participants: Participant[]): void {
// Update UI player list
}

private getLastJoinedPlayer(room: Room): Participant {
// Find most recently joined participant
return room.participants[room.participants.length - 1];
}

private getLastLeftPlayer(room: Room): Participant | null {
// Track who left (requires additional state tracking)
return null;
}

private getDisconnectedPlayer(room: Room): Participant | null {
// Find disconnected participant
return room.participants.find(p => !p.isConnected) || null;
}
}

Callback Patterns

State Management

const roomState = {
isConnected: false,
players: new Map(),
gameActive: false,
reconnectAttempts: 0
};

const callbacks = {
onConnectedToRoom: (room) => {
roomState.isConnected = true;
roomState.gameActive = true;
roomState.reconnectAttempts = 0;

// Initialize player states
room.participants.forEach(player => {
roomState.players.set(player.participantID, {
...player,
lastSeen: Date.now()
});
});
},

onDisconnectedFromRoom: (room) => {
roomState.isConnected = false;
roomState.gameActive = false;

// Mark all players as disconnected
roomState.players.forEach(player => {
player.isConnected = false;
});
},

onPeerJoined: (room, participantList) => {
// Add new player to state
const newPlayer = room.participants[room.participants.length - 1];
roomState.players.set(newPlayer.participantID, {
...newPlayer,
lastSeen: Date.now()
});
},

onPeerLeft: (room, participantList) => {
// Remove player from state
const leftPlayer = findLeftPlayer(room);
if (leftPlayer) {
roomState.players.delete(leftPlayer.participantID);
}
}
};

UI Updates

const uiCallbacks = {
onRoomCreated: (status, room) => {
if (status) {
showRoomScreen();
updateRoomInfo(room);
showInvitationDialog(room.roomID);
} else {
showError('Failed to create room');
}
},

onJoinedRoom: (status, room) => {
if (status) {
showGameScreen();
updatePlayerList(room.participants);
startLoadingGameAssets();
} else {
showError('Failed to join room');
}
},

onPeerJoined: (room, participantList) => {
const newPlayer = room.participants[room.participants.length - 1];
addPlayerToList(newPlayer);
showJoinNotification(newPlayer.name);
updatePlayerCount(participantList.length);
},

onPeerLeft: (room, participantList) => {
removePlayerFromList(findLeftPlayer(room));
updatePlayerCount(participantList.length);
showLeaveNotification();
},

attemptingReconnect: () => {
showReconnectingOverlay();
updateReconnectStatus('Attempting to reconnect...');
},

reconnectSuccessful: () => {
hideReconnectingOverlay();
showSuccessMessage('Reconnected successfully');
}
};

Error Handling

const errorHandlingCallbacks = {
onRoomCreated: (status, room) => {
if (!status) {
console.error('Room creation failed');
handleRoomCreationError(room?.msg);
}
},

onJoinedRoom: (status, room) => {
if (!status) {
console.error('Room join failed');
handleRoomJoinError(room?.msg);
}
},

onDisconnectedFromRoom: (room) => {
console.error('Unexpected disconnection');
handleUnexpectedDisconnection(room);
},

attemptingReconnect: () => {
roomState.reconnectAttempts++;

if (roomState.reconnectAttempts > MAX_RECONNECT_ATTEMPTS) {
console.error('Max reconnection attempts reached');
showFatalError('Unable to reconnect to room');
returnToMainMenu();
}
}
};

Advanced Patterns

Callback Chaining

function createCallbackChain() {
const callbacks = [];

return {
add: (callback) => callbacks.push(callback),
execute: (...args) => callbacks.forEach(cb => cb(...args))
};
}

// Usage
const onRoomCreatedChain = createCallbackChain();
onRoomCreatedChain.add((status, room) => updateUI(status, room));
onRoomCreatedChain.add((status, room) => logEvent(status, room));
onRoomCreatedChain.add((status, room) => analytics.track('room_created', { status }));

// In room creation
onRoomCreated: onRoomCreatedChain.execute

Conditional Callbacks

function createConditionalCallbacks(condition) {
return {
onRoomCreated: condition ? (status, room) => {
console.log('Conditional room created callback');
} : undefined,

onPeerJoined: condition ? (room, participantList) => {
console.log('Conditional peer joined callback');
} : undefined
};
}

// Usage
const isDebugMode = true;
const debugCallbacks = createConditionalCallbacks(isDebugMode);

MoitribeSDK('my-game', 'createendlessroom', {
...debugCallbacks,
// Other callbacks
});

Best Practices

Callback Cleanup

let roomCallbacks = null;

function createRoom() {
// Clean up previous callbacks
if (roomCallbacks) {
cleanupCallbacks(roomCallbacks);
}

roomCallbacks = {
onRoomCreated: handleRoomCreated,
onJoinedRoom: handleJoinedRoom,
// ... other callbacks
};

MoitribeSDK('my-game', 'createendlessroom', roomCallbacks);
}

function cleanupCallbacks(callbacks) {
// Cancel any pending operations
// Clear timers
// Reset state
console.log('Callbacks cleaned up');
}

Callback Debouncing

const debouncedCallbacks = {
onPeerJoined: debounce((room, participantList) => {
updatePlayerList(room.participants);
showJoinNotification();
}, 100),

onPeerLeft: debounce((room, participantList) => {
updatePlayerList(room.participants);
showLeaveNotification();
}, 100)
};

function debounce(func, wait) {
let timeout;
return function executedFunction(...args) {
const later = () => {
clearTimeout(timeout);
func(...args);
};
clearTimeout(timeout);
timeout = setTimeout(later, wait);
};
}

Next Steps

Pro Tip

Always implement proper error handling in callbacks, especially for network-related events like disconnections and reconnection attempts.