//===== Constructor const Connection = function() { this.retryCounter = 0; this.name = null; this.heartbeatTimer = null; this.socketListeners = { open: this.onOpen.bind(this), error: this.onError.bind(this), message: this.onReceiveMessage.bind(this) }; // Local event listeners document.addEventListener('L-join', (evt) => { this.name = evt.detail; this.connect(); }); document.addEventListener('L-newround', () => this.send({ type: 'newround' }) ); document.addEventListener('L-objective', () => this.send({ type: 'objective' }) ); document.addEventListener('L-robots', () => this.send({ type: 'robots' }) ); document.addEventListener('L-solve', (evt) => this.send({ type: 'solve', rawBody: evt.detail }) ); document.addEventListener('L-walls', () => this.send({ type: 'walls' }) ); document.addEventListener('L-skip', () => this.send({ type: 'skip' }) ); }; Connection.prototype.connect = function() { const hostname = window.location.hostname; const uuid = Cookie.getCookie({ name: 'player_id' }); this.ws = new WebSocket(`ws://${hostname}:8080/ricochet?name=${this.name}&uuid=${uuid}`); this.ws.addEventListener('open', this.socketListeners.open); this.ws.addEventListener('error', this.socketListeners.error); this.ws.addEventListener('message', this.socketListeners.message); }; Connection.prototype.send = function(json) { if (this.ws.readyState === 1) { this.ws.send(JSON.stringify(json)); } else { this.onError('WebSocket connection interrupted.'); } }; //===== Connection event handlers Connection.prototype.onOpen = function() { this.retryCounter = 0; clearTimeout(this.heartbeatTimer); this.heartbeatTimer = setTimeout(() => { this.ws.send({ type: 'heartbeat' }); }, 30000); const evt = new Event('L-conn-open'); document.dispatchEvent(evt); }; Connection.prototype.onError = function(err) { console.error(err); this.ws.removeEventListener('open', this.socketListeners.open); this.ws.removeEventListener('error', this.socketListeners.error); this.ws.removeEventListener('message', this.socketListeners.message); const evt = new CustomEvent('L-conn-error', { detail: err }); document.dispatchEvent(evt); if (this.retryCounter < 5) { this.retryCounter++; console.error("Retrying connection..."); setTimeout(this.connect.bind(this), Math.pow(this.retryCounter, 2) * 1000); } else { console.error("Retries exhausted. Server can't be contacted."); } }; Connection.prototype.onReceiveMessage = function({ data }) { const msg = JSON.parse(data); console.warn(JSON.stringify(msg)); if (!msg.type) { console.warn("Unprocessable message: ", msg) return; } let eventName; switch (msg.type) { case 'connected': eventName = 'G-connected'; break; case 'countdown': eventName = 'G-countdown'; break; case 'full': eventName = 'G-full'; break; case 'newround': eventName = 'G-newround'; break; case 'objective': eventName = 'G-objective'; break; case 'players': eventName = 'G-players'; break; case 'robots': eventName = 'G-robots'; break; case 'state': eventName = 'G-state'; break; case 'walls': eventName = 'G-walls'; break; case 'win': eventName = 'G-win'; break; } if (eventName) { const evt = new CustomEvent(eventName, { detail: msg }); document.dispatchEvent(evt); } };