WebSocket मल्टीप्लेयर की बुनियादी बातें
WebSockets के साथ एक मल्टीप्लेयर game बनाना: सर्वर आर्किटेक्चर, game rooms और state sync
WebSockets मल्टीप्लेयर games के लिए रियल-टाइम कम्युनिकेशन को संभव बनाते हैं। यह tutorial क्लाइंट-साइड की बुनियादी बातें कवर करता है।
1) सर्वर से कनेक्ट करना
js
const ws = new WebSocket('wss://game-server.example.com')
ws.onopen = () => {
console.log('Connected')
ws.send(JSON.stringify({ type: 'join', name: 'Player1' }))
}
ws.onmessage = (event) => {
const msg = JSON.parse(event.data)
handleMessage(msg)
}
ws.onclose = () => {
console.log('Disconnected')
}
ws.onerror = (err) => {
console.error('WebSocket error:', err)
}2) मैसेज प्रोटोकॉल डिज़ाइन
एक सरल प्रोटोकॉल तय करें:
js
// Client -> Server
{ type: 'join', name: 'Player1' }
{ type: 'input', keys: { left: true, jump: true } }
{ type: 'chat', message: 'Hello!' }
// Server -> Client
{ type: 'state', players: [...], entities: [...] }
{ type: 'playerJoined', id: 'abc', name: 'Player2' }
{ type: 'playerLeft', id: 'abc' }3) कनेक्शन मैनेजर क्लास
js
class GameConnection {
constructor(url) {
this.url = url
this.ws = null
this.handlers = new Map()
this.reconnectDelay = 1000
}
connect() {
this.ws = new WebSocket(this.url)
this.ws.onopen = () => {
this.reconnectDelay = 1000
this.emit('connected')
}
this.ws.onmessage = (e) => {
const msg = JSON.parse(e.data)
this.emit(msg.type, msg)
}
this.ws.onclose = () => {
this.emit('disconnected')
this.scheduleReconnect()
}
this.ws.onerror = () => {
this.ws.close()
}
}
scheduleReconnect() {
setTimeout(() => {
this.reconnectDelay = Math.min(this.reconnectDelay * 2, 30000)
this.connect()
}, this.reconnectDelay)
}
send(type, data = {}) {
if (this.ws?.readyState === WebSocket.OPEN) {
this.ws.send(JSON.stringify({ type, ...data }))
}
}
on(type, handler) {
if (!this.handlers.has(type)) {
this.handlers.set(type, [])
}
this.handlers.get(type).push(handler)
}
emit(type, data) {
const handlers = this.handlers.get(type) || []
handlers.forEach(h => h(data))
}
disconnect() {
this.ws?.close()
}
}4) प्लेयर input भेजना
positions नहीं, inputs भेजें। सर्वर को authoritative रहने दें:
js
const connection = new GameConnection('wss://server.example.com')
function sendInput(input) {
connection.send('input', {
left: input.left,
right: input.right,
jump: input.jump,
shoot: input.shoot,
seq: inputSequence++,
})
}
// Send input at a fixed rate (e.g., 20 times/sec)
setInterval(() => {
sendInput(currentInput)
}, 50)5) game state प्राप्त करना
js
let gameState = { players: [], entities: [] }
connection.on('state', (msg) => {
gameState = msg
})
connection.on('playerJoined', (msg) => {
gameState.players.push({ id: msg.id, name: msg.name })
})
connection.on('playerLeft', (msg) => {
gameState.players = gameState.players.filter(p => p.id !== msg.id)
})6) स्मूद मूवमेंट के लिए इंटरपोलेशन
सर्वर snapshots भेजता है। उनके बीच इंटरपोलेट करें:
js
class EntityInterpolator {
constructor() {
this.buffer = [] // { timestamp, state }
this.renderDelay = 100 // ms behind server
}
addSnapshot(timestamp, state) {
this.buffer.push({ timestamp, state })
// Keep only recent snapshots
while (this.buffer.length > 10) {
this.buffer.shift()
}
}
getState(currentTime) {
const renderTime = currentTime - this.renderDelay
// Find surrounding snapshots
let before = null
let after = null
for (let i = 0; i < this.buffer.length - 1; i++) {
if (this.buffer[i].timestamp <= renderTime &&
this.buffer[i + 1].timestamp >= renderTime) {
before = this.buffer[i]
after = this.buffer[i + 1]
break
}
}
if (!before || !after) {
return this.buffer[this.buffer.length - 1]?.state
}
// Interpolate
const t = (renderTime - before.timestamp) / (after.timestamp - before.timestamp)
return this.lerp(before.state, after.state, t)
}
lerp(a, b, t) {
return {
x: a.x + (b.x - a.x) * t,
y: a.y + (b.y - a.y) * t,
}
}
}7) क्लाइंट-साइड प्रेडिक्शन
रिस्पॉन्सिव कंट्रोल्स के लिए, लोकल रूप से प्रेडिक्ट करें और सर्वर के साथ reconcile करें:
js
class PredictedPlayer {
constructor() {
this.position = { x: 0, y: 0 }
this.pendingInputs = []
}
applyInput(input) {
// Apply input locally
if (input.left) this.position.x -= 5
if (input.right) this.position.x += 5
// Store for reconciliation
this.pendingInputs.push({ seq: input.seq, input })
}
reconcile(serverState, lastProcessedSeq) {
// Server confirmed position
this.position = { ...serverState.position }
// Remove confirmed inputs
this.pendingInputs = this.pendingInputs.filter(i => i.seq > lastProcessedSeq)
// Re-apply unconfirmed inputs
for (const pending of this.pendingInputs) {
if (pending.input.left) this.position.x -= 5
if (pending.input.right) this.position.x += 5
}
}
}8) लेटेंसी डिस्प्ले संभालना
js
class LatencyMonitor {
constructor(connection) {
this.connection = connection
this.pingStart = 0
this.latency = 0
connection.on('pong', () => {
this.latency = Date.now() - this.pingStart
})
setInterval(() => this.ping(), 1000)
}
ping() {
this.pingStart = Date.now()
this.connection.send('ping')
}
getLatency() {
return this.latency
}
}9) दक्षता के लिए बाइनरी मैसेज
हाई-फ्रीक्वेंसी अपडेट्स के लिए, बाइनरी का इस्तेमाल करें:
js
// Sending binary
const buffer = new ArrayBuffer(12)
const view = new DataView(buffer)
view.setFloat32(0, player.x)
view.setFloat32(4, player.y)
view.setUint32(8, inputFlags)
ws.send(buffer)
// Receiving binary
ws.binaryType = 'arraybuffer'
ws.onmessage = (e) => {
if (e.data instanceof ArrayBuffer) {
const view = new DataView(e.data)
const x = view.getFloat32(0)
const y = view.getFloat32(4)
// ...
}
}10) बेसिक सर्वर उदाहरण (Node.js)
js
import { WebSocketServer } from 'ws'
const wss = new WebSocketServer({ port: 8080 })
const players = new Map()
wss.on('connection', (ws) => {
const id = crypto.randomUUID()
players.set(id, { id, x: 0, y: 0, ws })
ws.on('message', (data) => {
const msg = JSON.parse(data)
if (msg.type === 'input') {
const player = players.get(id)
if (msg.left) player.x -= 5
if (msg.right) player.x += 5
}
})
ws.on('close', () => {
players.delete(id)
broadcast({ type: 'playerLeft', id })
})
})
// Broadcast state 20 times/sec
setInterval(() => {
const state = {
type: 'state',
players: Array.from(players.values()).map(p => ({
id: p.id, x: p.x, y: p.y
}))
}
broadcast(state)
}, 50)
function broadcast(msg) {
const data = JSON.stringify(msg)
for (const player of players.values()) {
player.ws.send(data)
}
}संबंधित
- तेज़ी से लोड होने वाला वेब game शिप करें
- Game input हैंडलिंग
- game logic के लिए Web Workers
- COOP/COEP और SharedArrayBuffer — मल्टीप्लेयर संदर्भों में SharedArrayBuffer के लिए जरूरी headers
- 2026 में को-ऑप Game डिज़ाइन — क्यों "friend slop" को-ऑप games फट रहे हैं और browser मल्टीप्लेयर इसमें कैसे फिट होता है
- Game Jams और Hackathons — मल्टीप्लेयर jam games को ज्यादा ध्यान मिलता है
बाहरी संसाधन
- MDN: WebSocket API — browser WebSocket रेफरेंस
- MDN: WebRTC API — कम लेटेंसी के लिए peer-to-peer कनेक्शन
- MDN: WebTransport API — एक नया HTTP/3 ट्रांसपोर्ट जो मार्च 2026 में Safari 26.4 के सपोर्ट शिप करने के बाद क्रॉस-browser Baseline तक पहुंच गया (यह पहले से ही Chrome 97+, Edge और Firefox 114+ में था)। एक अकेले TCP-आधारित WebSocket के विपरीत, WebTransport QUIC पर चलता है और unreliable datagrams के साथ-साथ कई स्वतंत्र streams देता है, इसलिए एक dropped packet बाकी सब कुछ को head-of-line-block नहीं करता। एक आम पैटर्न है हाई-फ्रीक्वेंसी position अपडेट्स को datagrams पर भेजना और chat या scoreboards को एक reliable stream पर रखना। WebSockets अभी भी सबसे ज्यादा compatible विकल्प हैं और ऊपर बताई गई हर बात अब भी लागू होती है, लेकिन लेटेंसी-सेंसिटिव games के लिए WebTransport पर विचार करना अब फायदेमंद है।
- Socket.IO documentation — लोकप्रिय WebSocket wrapper लाइब्रेरी
- Valve Source Multiplayer Networking — प्रोडक्शन games में इस्तेमाल होने वाले authoritative networking पैटर्न