mirror of
				https://github.com/bra1n/townsquare.git
				synced 2025-10-21 16:55:12 +00:00 
			
		
		
		
	moved latency calculation server-side
made host per channel exclusive added auto-reconnection
This commit is contained in:
		
							parent
							
								
									9e03d16160
								
							
						
					
					
						commit
						0b5d9c5865
					
				
					 4 changed files with 74 additions and 14 deletions
				
			
		|  | @ -17,6 +17,7 @@ const wss = new WebSocket.Server({ | |||
| function noop() {} | ||||
| 
 | ||||
| function heartbeat() { | ||||
|   this.latency = Math.round((new Date().getTime() - this.pingStart) / 2); | ||||
|   this.isAlive = true; | ||||
| } | ||||
| 
 | ||||
|  | @ -25,11 +26,32 @@ wss.on("connection", function connection(ws, req) { | |||
|     .split("/") | ||||
|     .pop() | ||||
|     .toLocaleLowerCase(); | ||||
|   if (ws.channel.match(/-host$/i)) { | ||||
|     ws.isHost = true; | ||||
|     ws.channel = ws.channel.substr(0, ws.channel.length - 5); | ||||
|     // check for another host on this channel
 | ||||
|     if ( | ||||
|       Array.from(wss.clients).some( | ||||
|         client => | ||||
|           client !== ws && | ||||
|           client.readyState === WebSocket.OPEN && | ||||
|           client.channel === ws.channel && | ||||
|           client.isHost | ||||
|       ) | ||||
|     ) { | ||||
|       console.log(ws.channel, "duplicate host"); | ||||
|       ws.close(1000, `The channel "${ws.channel}" already has a host`); | ||||
|       return; | ||||
|     } | ||||
|   } | ||||
|   ws.isAlive = true; | ||||
|   ws.pingStart = new Date().getTime(); | ||||
|   ws.ping(noop); | ||||
|   ws.on("pong", heartbeat); | ||||
|   ws.on("message", function incoming(data) { | ||||
|     if (!data.match(/^\["ping/i)) { | ||||
|       console.log(ws.channel, wss.clients.size, data); | ||||
|     const isPing = data.match(/^\["ping/i); | ||||
|     if (!isPing) { | ||||
|       console.log(new Date(), wss.clients.size, ws.channel, data); | ||||
|     } | ||||
|     wss.clients.forEach(function each(client) { | ||||
|       if ( | ||||
|  | @ -37,7 +59,12 @@ wss.on("connection", function connection(ws, req) { | |||
|         client.readyState === WebSocket.OPEN && | ||||
|         client.channel === ws.channel | ||||
|       ) { | ||||
|         client.send(data); | ||||
|         // inject latency between both clients if ping message
 | ||||
|         if (isPing && client.latency && ws.latency) { | ||||
|           client.send(data.replace(/latency/, client.latency + ws.latency)); | ||||
|         } else { | ||||
|           client.send(data); | ||||
|         } | ||||
|       } | ||||
|     }); | ||||
|   }); | ||||
|  | @ -47,6 +74,7 @@ const interval = setInterval(function ping() { | |||
|   wss.clients.forEach(function each(ws) { | ||||
|     if (ws.isAlive === false) return ws.terminate(); | ||||
|     ws.isAlive = false; | ||||
|     ws.pingStart = new Date().getTime(); | ||||
|     ws.ping(noop); | ||||
|   }); | ||||
| }, 30000); | ||||
|  |  | |||
|  | @ -3,7 +3,10 @@ | |||
|     <Screenshot ref="screenshot"></Screenshot> | ||||
|     <span | ||||
|       class="session" | ||||
|       :class="{ spectator: session.isSpectator }" | ||||
|       :class="{ | ||||
|         spectator: session.isSpectator, | ||||
|         reconnecting: session.isReconnecting | ||||
|       }" | ||||
|       v-if="session.sessionId" | ||||
|       @click="leaveSession" | ||||
|       :title=" | ||||
|  | @ -342,6 +345,16 @@ export default { | |||
|     &.spectator { | ||||
|       color: $townsfolk; | ||||
|     } | ||||
|     &.reconnecting { | ||||
|       animation: blink 1s infinite; | ||||
|     } | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| @keyframes blink { | ||||
|   50% { | ||||
|     opacity: 0.5; | ||||
|     color: gray; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -20,6 +20,7 @@ const handleVote = (state, [index, vote]) => { | |||
| const state = () => ({ | ||||
|   sessionId: "", | ||||
|   isSpectator: false, | ||||
|   isReconnecting: false, | ||||
|   playerCount: 0, | ||||
|   ping: 0, | ||||
|   playerId: "", | ||||
|  | @ -38,6 +39,7 @@ const mutations = { | |||
|   setSessionId: set("sessionId"), | ||||
|   setPlayerId: set("playerId"), | ||||
|   setSpectator: set("isSpectator"), | ||||
|   setReconnecting: set("isReconnecting"), | ||||
|   setPlayerCount: set("playerCount"), | ||||
|   setPing: set("ping"), | ||||
|   setVotingSpeed: set("votingSpeed"), | ||||
|  |  | |||
|  | @ -2,7 +2,7 @@ import rolesJSON from "../roles.json"; | |||
| 
 | ||||
| class LiveSession { | ||||
|   constructor(store) { | ||||
|     // this._wss = "ws://localhost:8081/";
 | ||||
|     //this._wss = "ws://localhost:8081/";
 | ||||
|     this._wss = "wss://baumgart.biz:8080/"; | ||||
|     this._socket = null; | ||||
|     this._isSpectator = true; | ||||
|  | @ -10,6 +10,7 @@ class LiveSession { | |||
|     this._store = store; | ||||
|     this._pingInterval = 30 * 1000; // 30 seconds between pings
 | ||||
|     this._pingTimer = null; | ||||
|     this._reconnectTimer = null; | ||||
|     this._players = {}; // map of players connected to a session
 | ||||
|     this._pings = {}; // map of player IDs to ping
 | ||||
|     // reconnect to previous session
 | ||||
|  | @ -25,14 +26,26 @@ class LiveSession { | |||
|    */ | ||||
|   _open(channel) { | ||||
|     this.disconnect(); | ||||
|     this._socket = new WebSocket(this._wss + channel); | ||||
|     this._socket = new WebSocket( | ||||
|       this._wss + channel + (this._isSpectator ? "" : "-host") | ||||
|     ); | ||||
|     this._socket.addEventListener("message", this._handleMessage.bind(this)); | ||||
|     this._socket.onopen = this._onOpen.bind(this); | ||||
|     this._socket.onclose = () => { | ||||
|     this._socket.onclose = err => { | ||||
|       this._socket = null; | ||||
|       this._store.commit("session/setSessionId", ""); | ||||
|       clearInterval(this._pingTimer); | ||||
|       this._pingTimer = null; | ||||
|       if (err.code !== 1000) { | ||||
|         // connection interrupted, reconnect after 3 seconds
 | ||||
|         this._store.commit("session/setReconnecting", true); | ||||
|         this._reconnectTimer = setTimeout( | ||||
|           () => this.connect(channel), | ||||
|           3 * 1000 | ||||
|         ); | ||||
|       } else { | ||||
|         this._store.commit("session/setSessionId", ""); | ||||
|         if (err.reason) alert(err.reason); | ||||
|       } | ||||
|     }; | ||||
|   } | ||||
| 
 | ||||
|  | @ -69,7 +82,7 @@ class LiveSession { | |||
|     this._send("ping", [ | ||||
|       this._isSpectator, | ||||
|       this._store.state.session.playerId, | ||||
|       new Date().getTime() | ||||
|       "latency" | ||||
|     ]); | ||||
|     this._handlePing(); | ||||
|     clearTimeout(this._pingTimer); | ||||
|  | @ -168,9 +181,11 @@ class LiveSession { | |||
|     this._pings = {}; | ||||
|     this._store.commit("session/setPlayerCount", 0); | ||||
|     this._store.commit("session/setPing", 0); | ||||
|     this._store.commit("session/setReconnecting", false); | ||||
|     clearTimeout(this._reconnectTimer); | ||||
|     if (this._socket) { | ||||
|       this._send("bye", this._store.state.session.playerId); | ||||
|       this._socket.close(); | ||||
|       this._socket.close(1000); | ||||
|       this._socket = null; | ||||
|     } | ||||
|   } | ||||
|  | @ -378,7 +393,7 @@ class LiveSession { | |||
|    * @param timestamp | ||||
|    * @private | ||||
|    */ | ||||
|   _handlePing([isSpectator, playerId, timestamp] = []) { | ||||
|   _handlePing([isSpectator, playerId, latency] = []) { | ||||
|     const now = new Date().getTime(); | ||||
|     // remove players that haven't sent a ping in twice the timespan
 | ||||
|     for (let player in this._players) { | ||||
|  | @ -404,10 +419,12 @@ class LiveSession { | |||
|         alert("Another storyteller joined the session!"); | ||||
|       } else if (this._isSpectator && !isSpectator) { | ||||
|         // ping to ST
 | ||||
|         this._store.commit("session/setPing", now - timestamp); | ||||
|       } else { | ||||
|         if (parseInt(latency, 10)) { | ||||
|           this._store.commit("session/setPing", parseInt(latency, 10)); | ||||
|         } | ||||
|       } else if(parseInt(latency, 10)) { | ||||
|         // ping to Players
 | ||||
|         this._pings[playerId] = now - timestamp; | ||||
|         this._pings[playerId] = parseInt(latency, 10); | ||||
|         const pings = Object.values(this._pings); | ||||
|         this._store.commit( | ||||
|           "session/setPing", | ||||
|  |  | |||
		Loading…
	
	Add table
		
		Reference in a new issue