Connection Callbacks
The Moitribe SDK provides connection state monitoring through callbacks, allowing you to handle network interruptions, reconnections, and connection failures gracefully.
Connection Callback Interface
The SDK supports four connection-related callbacks:
interface ConnectionCallbacks {
onConnectionLost?: (errorCode: number, errorMsg: string) => void;
onConnected?: (isReconnect: boolean, URI: string) => void;
onSuccessConnect?: () => void;
onFailureConnect?: () => void;
}
Basic Setup
Initialize with Connection Callbacks
const connectionCallbacks = {
onConnectionLost: (errorCode: number, errorMsg: string) => {
console.error('Connection lost:', errorCode, errorMsg);
showConnectionError(errorMsg);
},
onConnected: (isReconnect: boolean, URI: string) => {
console.log('Connected to:', URI);
if (isReconnect) {
console.log('This is a reconnection');
refreshGameData();
}
hideConnectionError();
},
onSuccessConnect: () => {
console.log('Connection established successfully');
enableGameFeatures();
},
onFailureConnect: () => {
console.error('Failed to establish connection');
showConnectionFailedMessage();
}
};
// Initialize SDK with connection callbacks
MoitribeSDK('my-game', 'init', {
connectionCallbacks: connectionCallbacks
}, (result) => {
console.log('SDK initialization:', result.success);
});
Advanced Connection Management
Connection State Manager
enum ConnectionState {
DISCONNECTED = 'disconnected',
CONNECTING = 'connecting',
CONNECTED = 'connected',
RECONNECTING = 'reconnecting',
FAILED = 'failed'
}
class ConnectionManager {
private static state: ConnectionState = ConnectionState.DISCONNECTED;
private static reconnectAttempts: number = 0;
private static maxReconnectAttempts: number = 5;
private static reconnectDelay: number = 1000;
private static listeners: Array<(state: ConnectionState) => void> = [];
static getState(): ConnectionState {
return this.state;
}
static setState(newState: ConnectionState): void {
const oldState = this.state;
this.state = newState;
console.log(`Connection state: ${oldState} → ${newState}`);
this.notifyListeners(newState);
// Reset reconnect attempts on successful connection
if (newState === ConnectionState.CONNECTED) {
this.reconnectAttempts = 0;
}
}
static addListener(listener: (state: ConnectionState) => void): void {
this.listeners.push(listener);
}
static removeListener(listener: (state: ConnectionState) => void): void {
const index = this.listeners.indexOf(listener);
if (index > -1) {
this.listeners.splice(index, 1);
}
}
private static notifyListeners(state: ConnectionState): void {
this.listeners.forEach(listener => {
try {
listener(state);
} catch (error) {
console.error('Error in connection state listener:', error);
}
});
}
static shouldAttemptReconnect(): boolean {
return this.reconnectAttempts < this.maxReconnectAttempts;
}
static incrementReconnectAttempts(): void {
this.reconnectAttempts++;
}
static getReconnectDelay(): number {
// Exponential backoff
return this.reconnectDelay * Math.pow(2, this.reconnectAttempts - 1);
}
}
// Usage
ConnectionManager.addListener((state) => {
updateConnectionUI(state);
});
Enhanced Connection Callbacks
class EnhancedConnectionCallbacks {
private static gameId: string = '';
private static reconnectTimer: NodeJS.Timeout | null = null;
static create(gameId: string): ConnectionCallbacks {
this.gameId = gameId;
return {
onConnectionLost: (errorCode: number, errorMsg: string) => {
ConnectionManager.setState(ConnectionState.DISCONNECTED);
this.handleConnectionLost(errorCode, errorMsg);
},
onConnected: (isReconnect: boolean, URI: string) => {
ConnectionManager.setState(ConnectionState.CONNECTED);
this.handleConnected(isReconnect, URI);
},
onSuccessConnect: () => {
ConnectionManager.setState(ConnectionState.CONNECTED);
this.handleConnectionSuccess();
},
onFailureConnect: () => {
ConnectionManager.setState(ConnectionState.FAILED);
this.handleConnectionFailure();
}
};
}
private static handleConnectionLost(errorCode: number, errorMsg: string): void {
console.error('Connection lost:', { errorCode, errorMsg });
// Clear any existing reconnect timer
if (this.reconnectTimer) {
clearTimeout(this.reconnectTimer);
this.reconnectTimer = null;
}
// Show user notification
this.showConnectionNotification('Connection lost. Attempting to reconnect...', 'warning');
// Attempt reconnection if appropriate
if (ConnectionManager.shouldAttemptReconnect()) {
ConnectionManager.setState(ConnectionState.RECONNECTING);
this.scheduleReconnect();
} else {
this.showConnectionNotification('Connection failed. Please refresh the page.', 'error');
}
}
private static handleConnected(isReconnect: boolean, URI: string): void {
console.log('Connected to server:', { isReconnect, URI });
if (isReconnect) {
this.showConnectionNotification('Reconnected successfully', 'success');
this.refreshGameData();
} else {
this.showConnectionNotification('Connected to game server', 'success');
}
// Clear reconnect timer
if (this.reconnectTimer) {
clearTimeout(this.reconnectTimer);
this.reconnectTimer = null;
}
}
private static handleConnectionSuccess(): void {
console.log('Connection established successfully');
this.enableGameFeatures();
}
private static handleConnectionFailure(): void {
console.error('Failed to establish connection');
this.showConnectionNotification('Failed to connect to game server', 'error');
this.disableGameFeatures();
}
private static scheduleReconnect(): void {
if (!ConnectionManager.shouldAttemptReconnect()) {
return;
}
ConnectionManager.incrementReconnectAttempts();
const delay = ConnectionManager.getReconnectDelay();
console.log(`Scheduling reconnect attempt ${ConnectionManager['reconnectAttempts']} in ${delay}ms`);
this.reconnectTimer = setTimeout(() => {
this.attemptReconnect();
}, delay);
}
private static attemptReconnect(): void {
console.log(`Attempting reconnect (${ConnectionManager['reconnectAttempts']}/${ConnectionManager['maxReconnectAttempts']})`);
// Reinitialize the SDK
MoitribeSDK(this.gameId, 'init', {
connectionCallbacks: this.create(this.gameId)
}, (result) => {
if (!result.success) {
// Reconnect failed, schedule another attempt
this.scheduleReconnect();
}
});
}
private static refreshGameData(): void {
// Refresh critical game data after reconnection
console.log('Refreshing game data after reconnection');
// Reload player profile
MoitribeSDK(this.gameId, 'getprofile', {}, (result) => {
if (result.success) {
console.log('Profile refreshed after reconnection');
}
});
// Reload leaderboard data if needed
MoitribeSDK(this.gameId, 'loadleaderboardmetadata', {
leaderboardid: 'main'
}, (result) => {
if (result.success) {
console.log('Leaderboard refreshed after reconnection');
}
});
}
private static showConnectionNotification(message: string, type: 'success' | 'warning' | 'error'): void {
// Implementation depends on your UI framework
console.log(`[${type.toUpperCase()}] ${message}`);
// Example: Show toast notification
if (typeof window !== 'undefined' && (window as any).showToast) {
(window as any).showToast(message, type);
}
}
private static enableGameFeatures(): void {
// Enable game features that require connection
console.log('Enabling game features');
}
private static disableGameFeatures(): void {
// Disable game features that require connection
console.log('Disabling game features');
}
}
UI Integration
React Component Example
import React, { useState, useEffect } from 'react';
import { ConnectionState } from './ConnectionManager';
const ConnectionStatus: React.FC = () => {
const [connectionState, setConnectionState] = useState<ConnectionState>(ConnectionState.DISCONNECTED);
const [isVisible, setIsVisible] = useState(false);
useEffect(() => {
ConnectionManager.addListener(setConnectionState);
return () => {
ConnectionManager.removeListener(setConnectionState);
};
}, []);
useEffect(() => {
// Show connection status only when not connected
setIsVisible(connectionState !== ConnectionState.CONNECTED);
const timer = setTimeout(() => {
setIsVisible(false);
}, 5000);
return () => clearTimeout(timer);
}, [connectionState]);
if (!isVisible) return null;
const getStatusColor = () => {
switch (connectionState) {
case ConnectionState.CONNECTED: return 'green';
case ConnectionState.CONNECTING:
case ConnectionState.RECONNECTING: return 'orange';
case ConnectionState.FAILED:
case ConnectionState.DISCONNECTED: return 'red';
default: return 'gray';
}
};
const getStatusText = () => {
switch (connectionState) {
case ConnectionState.CONNECTED: return 'Connected';
case ConnectionState.CONNECTING: return 'Connecting...';
case ConnectionState.RECONNECTING: return 'Reconnecting...';
case ConnectionState.FAILED: return 'Connection Failed';
case ConnectionState.DISCONNECTED: return 'Disconnected';
default: return 'Unknown';
}
};
return (
<div style={{
position: 'fixed',
top: 20,
right: 20,
backgroundColor: getStatusColor(),
color: 'white',
padding: '10px 15px',
borderRadius: '5px',
fontSize: '14px',
fontWeight: 'bold',
zIndex: 1000
}}>
{getStatusText()}
</div>
);
};
export default ConnectionStatus;
Vanilla JavaScript Example
class ConnectionUI {
private static element: HTMLElement | null = null;
static initialize(): void {
// Create connection status element
this.element = document.createElement('div');
this.element.id = 'connection-status';
this.element.style.cssText = `
position: fixed;
top: 20px;
right: 20px;
padding: 10px 15px;
border-radius: 5px;
font-size: 14px;
font-weight: bold;
color: white;
z-index: 1000;
display: none;
transition: all 0.3s ease;
`;
document.body.appendChild(this.element);
// Listen to connection state changes
ConnectionManager.addListener((state) => {
this.updateUI(state);
});
}
private static updateUI(state: ConnectionState): void {
if (!this.element) return;
const colors = {
[ConnectionState.CONNECTED]: '#4CAF50',
[ConnectionState.CONNECTING]: '#FF9800',
[ConnectionState.RECONNECTING]: '#FF9800',
[ConnectionState.FAILED]: '#F44336',
[ConnectionState.DISCONNECTED]: '#F44336'
};
const messages = {
[ConnectionState.CONNECTED]: 'Connected',
[ConnectionState.CONNECTING]: 'Connecting...',
[ConnectionState.RECONNECTING]: 'Reconnecting...',
[ConnectionState.FAILED]: 'Connection Failed',
[ConnectionState.DISCONNECTED]: 'Disconnected'
};
this.element.style.backgroundColor = colors[state];
this.element.textContent = messages[state];
this.element.style.display = 'block';
// Auto-hide when connected
if (state === ConnectionState.CONNECTED) {
setTimeout(() => {
if (this.element) {
this.element.style.display = 'none';
}
}, 3000);
}
}
static showNotification(message: string, type: 'success' | 'warning' | 'error'): void {
if (!this.element) return;
const colors = {
success: '#4CAF50',
warning: '#FF9800',
error: '#F44336'
};
this.element.style.backgroundColor = colors[type];
this.element.textContent = message;
this.element.style.display = 'block';
setTimeout(() => {
if (this.element) {
this.element.style.display = 'none';
}
}, 5000);
}
}
// Initialize when DOM is ready
if (typeof document !== 'undefined') {
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', () => {
ConnectionUI.initialize();
});
} else {
ConnectionUI.initialize();
}
}
Real-Time Multiplayer Connection Handling
Room Connection Management
class RoomConnectionManager {
private static currentRoomId: string | null = null;
private static isReconnecting: boolean = false;
static handleRoomConnection(roomId: string): void {
this.currentRoomId = roomId;
console.log('Connected to room:', roomId);
}
static handleRoomDisconnection(): void {
if (this.currentRoomId && !this.isReconnecting) {
console.log('Disconnected from room:', this.currentRoomId);
this.attemptRoomReconnection();
}
}
private static attemptRoomReconnection(): void {
if (!this.currentRoomId) return;
this.isReconnecting = true;
console.log('Attempting to rejoin room:', this.currentRoomId);
// Wait for main connection to be restored
const checkConnection = () => {
if (ConnectionManager.getState() === ConnectionState.CONNECTED) {
// Try to rejoin the room
MoitribeSDK('my-game', 'joinstandardroominvcode', {
invitationcode: this.currentRoomId!
}, (result) => {
this.isReconnecting = false;
if (result.success) {
console.log('Successfully rejoined room:', this.currentRoomId);
} else {
console.error('Failed to rejoin room:', result.message);
// Could show UI to user asking them to manually rejoin
}
});
} else {
// Check again in 1 second
setTimeout(checkConnection, 1000);
}
};
checkConnection();
}
static leaveRoom(): void {
this.currentRoomId = null;
this.isReconnecting = false;
}
}
Testing Connection Handling
Connection Simulation
class ConnectionSimulator {
static simulateConnectionLoss(duration: number = 5000): void {
console.log('Simulating connection loss...');
// Trigger connection lost callback
const callbacks = EnhancedConnectionCallbacks.create('my-game');
callbacks.onConnectionLost?.(-1, 'Simulated connection loss');
// Simulate reconnection after duration
setTimeout(() => {
console.log('Simulating reconnection...');
callbacks.onConnected?.(true, 'ws:// simulated-server');
}, duration);
}
static simulateConnectionFailure(): void {
console.log('Simulating connection failure...');
const callbacks = EnhancedConnectionCallbacks.create('my-game');
callbacks.onFailureConnect?.();
}
static testReconnectLogic(): void {
console.log('Testing reconnect logic...');
// Simulate multiple connection losses
this.simulateConnectionLoss(2000);
setTimeout(() => this.simulateConnectionLoss(3000), 8000);
setTimeout(() => this.simulateConnectionLoss(2000), 15000);
}
}
// Enable simulation in development
if (process.env.NODE_ENV === 'development') {
(window as any).connectionSimulator = ConnectionSimulator;
console.log('Connection simulator available at: window.connectionSimulator');
}
Best Practices
1. Always Handle Connection States
// ✓ Good - handle all connection states
const callbacks = {
onConnectionLost: (code, msg) => handleDisconnection(code, msg),
onConnected: (isReconnect, uri) => handleConnection(isReconnect, uri),
onSuccessConnect: () => handleSuccess(),
onFailureConnect: () => handleFailure()
};
// ✗ Bad - only handling success
const callbacks = {
onSuccessConnect: () => console.log('Connected')
};
2. Provide User Feedback
// ✓ Good - user-friendly messages
const showConnectionMessage = (message: string, type: 'info' | 'warning' | 'error') => {
// Show toast, banner, or other UI element
showToast(message, type);
};
// ✗ Bad - silent failures
const callbacks = {
onConnectionLost: () => {} // No user feedback
};
3. Implement Exponential Backoff
// ✓ Good - exponential backoff for reconnection
const getReconnectDelay = (attempt: number) => {
return Math.min(1000 * Math.pow(2, attempt), 30000); // Max 30 seconds
};
// ✗ Bad - fixed interval
const getReconnectDelay = () => 1000; // Always 1 second
4. Handle Room Reconnection
// ✓ Good - handle room reconnection after main connection
const handleReconnected = () => {
if (currentRoomId) {
rejoinRoom(currentRoomId);
}
refreshGameData();
};
// ✗ Bad - only handle main connection
const handleReconnected = () => {
console.log('Reconnected'); // No room handling
};
Next Steps
- Error Handling - Handle connection-related errors
- Callback Patterns - Understanding callback usage
- Real-Time Multiplayer - Room connection management
warning
Always test your connection handling logic with network simulations to ensure it works reliably in poor network conditions.