Skip to main content

Room Lifecycle and States

Standard Rooms follow a well-defined lifecycle with distinct states. Understanding these states and transitions helps you create robust multiplayer experiences and handle edge cases effectively.

Room States

Standard Rooms progress through these states:

StateValueDescriptionTypical Duration
AUTO_MATCHING1Room is in matchmaking pool0-60 seconds
CONNECTING2Players are connecting to each other5-15 seconds
ACTIVE3Room is fully connected and ready for gameplayUntil game ends

State Transitions

Room Created → AUTO_MATCHING → CONNECTING → ACTIVE → Room Ended
↓ ↓ ↓ ↓
Private Room Players Found All Connected Game Complete
(Optional) (Auto-match) (P2P Setup) (Leave/Finish)

Lifecycle Phases

1. Room Creation Phase

The room is created and enters the initial state:

// Room creation triggers onRoomCreated callback
onRoomCreated: (status, room) => {
if (status) {
console.log('Room created with state:', room.status);

switch (room.status) {
case 1: // AUTO_MATCHING
console.log('Room is in auto-matching pool');
showAutoMatchScreen(room);
startMatchmakingTimer(room.autoMatchWaitSeconds);
break;

case 2: // CONNECTING
console.log('Room is in connecting phase');
showConnectingScreen();
break;

case 3: // ACTIVE
console.log('Room is already active');
showActiveGameScreen(room);
break;
}

// Store initial room state
currentRoomState = room.status;
previousRoomState = null;
}
}

2. Auto-Matching Phase

Room waits for players to join via auto-matching:

// Handle auto-matching state
function handleAutoMatchingState(room) {
console.log('Handling auto-matching state');

// Show matchmaking UI
showMatchmakingUI({
waitTime: room.autoMatchWaitSeconds,
currentPlayers: room.participants.length,
minPlayers: room.min_automatch_players,
maxPlayers: room.max_automatch_players
});

// Start matchmaking timer
startMatchmakingTimer(room.autoMatchWaitSeconds, () => {
console.log('Matchmaking timeout - extending search');
extendMatchmakingSearch();
});

// Monitor player joins
room.participants.forEach(participant => {
console.log('Player in matchmaking:', participant.name);
});

// Update UI as players join
updateMatchmakingProgress(room.participants.length, room.min_automatch_players);
}

// Called when onPeerJoined fires during auto-matching
onPeerJoined: (room, participantList) => {
if (room.status === 1) { // Still auto-matching
console.log('New player joined during matchmaking');

// Update progress
updateMatchmakingProgress(participantList.length, room.min_automatch_players);

// Check if ready to transition
if (participantList.length >= room.min_automatch_players) {
console.log('Minimum players reached - preparing to connect');
showTransitionToConnecting();
}
}
}

3. Connecting Phase

Players establish peer-to-peer connections:

// Handle connecting state
function handleConnectingState(room) {
console.log('Handling connecting state');

// Show connecting UI
showConnectingUI({
totalPlayers: room.participants.length,
connectedPlayers: room.participants.filter(p => p.isConnected).length
});

// Start connection timeout
startConnectionTimer(30, () => {
console.error('Connection timeout');
handleConnectionTimeout();
});

// Monitor connection progress
monitorConnectionProgress(room);

// Show connection tips
showConnectionTips();
}

// Monitor individual player connections
function monitorConnectionProgress(room) {
room.participants.forEach(participant => {
if (participant.isConnected) {
console.log(`${participant.name} is connected`);
} else {
console.log(`${participant.name} is connecting...`);
}
});

// Check if all players are connected
const allConnected = room.participants.every(p => p.isConnected);
if (allConnected) {
console.log('All players connected - ready for active state');
prepareForActiveState();
}
}

// Handle connection state changes
onPeerConnected: (room, participantList) => {
if (room.status === 2) { // Still connecting
console.log('Player connected during connecting phase');

// Update connection progress
updateConnectionProgress(
participantList.length,
room.participants.length
);

// Check if ready to transition to active
if (participantList.length === room.participants.length) {
console.log('All players connected - transitioning to active');
showTransitionToActive();
}
}
}

4. Active Phase

Room is fully connected and ready for gameplay:

// Handle active state
function handleActiveState(room) {
console.log('Handling active state - game ready!');

// Hide all transition screens
hideAllTransitionScreens();

// Show game UI
showGameUI(room);

// Enable game controls
enableGameControls();

// Start game loop
startGameLoop();

// Begin game state synchronization
startGameStateSync();

// Show game start notification
showGameStartNotification();

// Log game start for analytics
logGameStart(room);

// Start game timer
startGameTimer();

// Enable voice chat if available
if (isVoiceChatAvailable()) {
enableVoiceChat();
}
}

// Called when onRoomConnected fires
onRoomConnected: (status, room) => {
if (status) {
console.log('Room connected - entering active state');

// Verify room is in active state
if (room.status === 3) {
handleActiveState(room);
} else {
console.warn('Room connected but not in active state:', room.status);
// Handle unexpected state
handleUnexpectedRoomState(room);
}
} else {
console.error('Room connection failed');
handleConnectionFailure();
}
}

State Management

Track State Changes

// State tracking variables
let currentRoomState = null;
let previousRoomState = null;
let stateHistory = [];

// Monitor state changes
function monitorRoomState(room) {
const newState = room.status;

if (newState !== currentRoomState) {
console.log(`Room state transition: ${currentRoomState}${newState}`);

// Record state change
previousRoomState = currentRoomState;
currentRoomState = newState;
stateHistory.push({
from: previousRoomState,
to: newState,
timestamp: Date.now()
});

// Handle state transition
handleStateTransition(previousRoomState, newState, room);
}
}

// Handle specific transitions
function handleStateTransition(fromState, toState, room) {
console.log(`Handling transition: ${fromState}${toState}`);

// Auto-matching → Connecting
if (fromState === 1 && toState === 2) {
console.log('Players found - establishing connections');
showTransitionScreen('Connecting Players...');
startConnectionPhase();
}

// Connecting → Active
else if (fromState === 2 && toState === 3) {
console.log('All connected - starting game');
showTransitionScreen('Starting Game...');
prepareGameStart();
}

// Any → Auto-matching (re-matching)
else if (toState === 1 && fromState !== null) {
console.log('Returning to auto-matching');
showTransitionScreen('Searching for Players...');
resetMatchmaking();
}
}

// Call this in all room callbacks
function updateRoomState(room) {
monitorRoomState(room);
updateUIForCurrentState(room);
}

State-Based UI Updates

// Update UI based on current room state
function updateUIForCurrentState(room) {
switch (room.status) {
case 1: // AUTO_MATCHING
showMatchmakingUI();
hideGameUI();
hideConnectingUI();
break;

case 2: // CONNECTING
showConnectingUI();
hideMatchmakingUI();
hideGameUI();
break;

case 3: // ACTIVE
showGameUI();
hideMatchmakingUI();
hideConnectingUI();
break;

default:
console.warn('Unknown room state:', room.status);
showErrorUI('Unknown room state');
}

// Update common elements
updatePlayerCount(room.participants.length);
updateRoomID(room.roomID);
updateConnectionStatus(room.status);
}

// State-specific UI components
function showMatchmakingUI() {
document.getElementById('matchmaking-screen').style.display = 'block';
document.getElementById('waiting-text').textContent = 'Searching for players...';
startMatchmakingAnimation();
}

function showConnectingUI() {
document.getElementById('connecting-screen').style.display = 'block';
document.getElementById('connecting-text').textContent = 'Establishing connections...';
startConnectingAnimation();
}

function showGameUI() {
document.getElementById('game-screen').style.display = 'block';
document.getElementById('game-hud').style.display = 'block';
initializeGameHUD();
}

Error States and Recovery

Handle State Transitions Failures

// Handle failed state transitions
function handleStateTransitionFailure(fromState, toState, error) {
console.error(`State transition failed: ${fromState}${toState}`, error);

// Show error to user
showStateTransitionError(fromState, toState, error);

// Attempt recovery based on current state
switch (fromState) {
case 1: // Failed during auto-matching
console.log('Auto-matching failed - retrying');
retryAutoMatching();
break;

case 2: // Failed during connecting
console.log('Connection failed - attempting reconnection');
attemptReconnection();
break;

case 3: // Failed during active game
console.log('Active game failed - attempting recovery');
attemptGameRecovery();
break;
}
}

// Recovery strategies
function retryAutoMatching() {
showRetryScreen('Retrying matchmaking...');

setTimeout(() => {
// Restart matchmaking with expanded criteria
restartMatchmaking({
expandSearch: true,
increaseWaitTime: true
});
}, 2000);
}

function attemptReconnection() {
showReconnectionScreen('Attempting to reconnect...');

let attempts = 0;
const maxAttempts = 5;

const reconnectInterval = setInterval(() => {
attempts++;
updateReconnectionProgress(attempts, maxAttempts);

if (attempts >= maxAttempts) {
clearInterval(reconnectInterval);
handleReconnectionFailure();
return;
}

// Attempt reconnection
attemptRoomReconnect();
}, 3000);
}

Best Practices

State Management

  • Track state changes systematically
  • Log state transitions for debugging
  • Handle unexpected states gracefully
  • Implement proper state recovery

User Experience

  • Show clear feedback during transitions
  • Use appropriate loading animations
  • Provide progress indicators
  • Handle timeouts gracefully

Error Handling

  • Implement retry mechanisms
  • Provide fallback options
  • Log errors for debugging
  • Maintain data consistency

Performance

  • Minimize UI updates during transitions
  • Use efficient state tracking
  • Avoid blocking operations
  • Clean up resources properly

Next Steps