Reliable vs Unreliable Messaging
In real-time multiplayer games, you can choose between reliable and unreliable message delivery based on your game's requirements. Each type has specific use cases and performance characteristics.
Message Reliability Types
Reliable Messages
Reliable messages are guaranteed to be delivered in order, but may have higher latency due to retransmission and acknowledgment mechanisms.
Use Cases:
- Game state changes
- Turn-based actions
- Chat messages
- Score updates
- Critical game events
Unreliable Messages
Unreliable messages are sent without guarantees of delivery or order, providing lower latency but potentially dropping packets.
Use Cases:
- Player position updates
- Continuous movement data
- Transient game states
- Real-time animations
- Frequent sensor data
Implementation
The SDK provides message reliability through the isReliable parameter when sending messages.
JavaScript Example
// Send reliable message (game state change)
MoitribeSDK('my-game', 'sendmsg', {
message: gameStateData,
isReliable: true
}, (result) => {
console.log('Reliable message sent:', result.success);
});
// Send unreliable message (position update)
MoitribeSDK('my-game', 'sendmsg', {
message: positionData,
isReliable: false
}, (result) => {
console.log('Unreliable message sent:', result.success);
});
TypeScript Example
import MoitribeSDK from '@veniso/moitribe-js';
interface GameState {
playerTurn: string;
action: 'move' | 'attack' | 'defend';
}
interface Position {
x: number;
y: number;
z: number;
}
// Send reliable message with typed data
const gameState: GameState = {
playerTurn: 'player1',
action: 'move'
};
MoitribeSDK('my-game', 'sendmsg', {
message: JSON.stringify(gameState),
isReliable: true
}, (result: { success: boolean }) => {
console.log('Game state sent:', result.success);
});
// Send unreliable message with position data
const position: Position = { x: 100, y: 50, z: 0 };
MoitribeSDK('my-game', 'sendmsg', {
message: JSON.stringify(position),
isReliable: false
}, (result: { success: boolean }) => {
console.log('Position update sent:', result.success);
});
Receiving Messages
When receiving messages, the isReliable parameter indicates how the message was sent:
// Message callback setup
const messageCallback = (messageData, senderParticipantID, isReliable) => {
console.log(`Message from ${senderParticipantID}`);
console.log(`Reliable: ${isReliable}`);
if (isReliable) {
// Handle critical game data
const gameState = JSON.parse(messageData);
processGameState(gameState);
} else {
// Handle transient data like positions
const position = JSON.parse(messageData);
updatePlayerPosition(senderParticipantID, position);
}
};
Performance Considerations
Network Impact
| Message Type | Latency | Bandwidth | CPU Usage |
|---|---|---|---|
| Reliable | Higher | Higher | Higher |
| Unreliable | Lower | Lower | Lower |
When to Use Each Type
Use reliable messages for anything that affects game logic or player experience if lost. Use unreliable messages for data that can be safely discarded or quickly outdated.
Choose Reliable for:
- Game-critical events (player death, level completion)
- Turn-based game moves
- Chat and communication
- Score and achievement updates
- Inventory changes
Choose Unreliable for:
- Real-time position updates (30+ times per second)
- Animation states
- Temporary visual effects
- Sensor data that becomes stale quickly
- Redundant data streams
Hybrid Approach
Many games use both types strategically:
// Example: Real-time action game
function sendGameUpdate() {
// Unreliable: Position (sent frequently)
MoitribeSDK('my-game', 'sendmsg', {
message: JSON.stringify({
type: 'position',
x: player.x,
y: player.y
}),
isReliable: false
});
// Reliable: Action (sent only when needed)
if (player.actionChanged) {
MoitribeSDK('my-game', 'sendmsg', {
message: JSON.stringify({
type: 'action',
action: player.currentAction
}),
isReliable: true
});
player.actionChanged = false;
}
}
Common Patterns
Interpolation for Unreliable Data
Since unreliable messages may be dropped, implement interpolation:
let lastPosition = { x: 0, y: 0 };
let targetPosition = { x: 0, y: 0 };
function handlePositionUpdate(data) {
lastPosition = { ...targetPosition };
targetPosition = JSON.parse(data);
}
function interpolate() {
// Smooth movement between last and target positions
const currentX = lastPosition.x + (targetPosition.x - lastPosition.x) * 0.1;
const currentY = lastPosition.y + (targetPosition.y - lastPosition.y) * 0.1;
renderPlayer(currentX, currentY);
requestAnimationFrame(interpolate);
}
Acknowledgment for Critical Messages
For extra reliability, implement acknowledgment patterns:
const pendingMessages = new Map();
function sendCriticalMessage(data) {
const messageId = generateId();
pendingMessages.set(messageId, {
data,
timestamp: Date.now(),
retries: 0
});
MoitribeSDK('my-game', 'sendmsg', {
message: JSON.stringify({
id: messageId,
...data
}),
isReliable: true
});
}
function handleAcknowledgment(data) {
const { messageId } = JSON.parse(data);
pendingMessages.delete(messageId);
}
Error Handling
MoitribeSDK('my-game', 'sendmsg', {
message: gameData,
isReliable: true
}, (result) => {
if (!result.success) {
console.error('Message failed:', result.msg);
// Retry logic for critical messages
if (result.msg.includes('connection')) {
setTimeout(() => {
sendReliableMessage(gameData);
}, 1000);
}
}
});
Next Steps
- Message Format - Understanding ArrayBuffer message structure
- Encoding/Decoding - Serializing game data efficiently
- Message Best Practices - Performance optimization tips