From 533f2898871cc5c1a548308537fbcd4f8d4bbdf5 Mon Sep 17 00:00:00 2001 From: "(quasar) nebula" Date: Fri, 23 Apr 2021 11:44:58 -0300 Subject: identifier per socket connection --- backend.js | 8 ++++---- socket.js | 60 +++++++++++++++++++++++++++++++++++++++++++----------------- todo.txt | 2 ++ ui.js | 8 ++++---- 4 files changed, 53 insertions(+), 25 deletions(-) diff --git a/backend.js b/backend.js index ddfb000..13287ec 100644 --- a/backend.js +++ b/backend.js @@ -859,16 +859,16 @@ class Backend extends EventEmitter { this.emit('log message', messageInfo) } - loadPartyGrouplike(partyGrouplike) { - this.emit('got party grouplike', partyGrouplike) + loadPartyGrouplike(socketId, partyGrouplike) { + this.emit('got party grouplike', socketId, partyGrouplike) } shareWithParty(item) { this.emit('share with party', item) } - partyGrouplikeUpdated(partyGrouplike) { - this.emit('party grouplike updated', partyGrouplike) + partyGrouplikeUpdated(socketId, partyGrouplike) { + this.emit('party grouplike updated', socketId, partyGrouplike) } } diff --git a/socket.js b/socket.js index 92a48bf..3db0f98 100644 --- a/socket.js +++ b/socket.js @@ -28,6 +28,7 @@ const originalSymbol = Symbol('Original item') const EventEmitter = require('events') const net = require('net') +const shortid = require('shortid') const { saveBackend, @@ -110,6 +111,8 @@ function validateCommand(command) { switch (command.code) { case 'initialize-backend': return typeof command.backend === 'object' + case 'set-socket-id': + return typeof command.socketId === 'string' } // No break here; servers can send commands which typically come from // clients too. @@ -243,7 +246,7 @@ function makeSocketServer() { // that's the responsibility of the caller (use server.listen()). const server = new net.Server() - const sockets = [] + const socketMap = Object.create(null) server.canonicalBackend = null @@ -252,13 +255,15 @@ function makeSocketServer() { const donePlaying = {} server.on('connection', socket => { - sockets.push(socket) + const socketId = shortid.generate() + + socketMap[socketId] = socket let nickname = DEFAULT_NICKNAME socket.on('close', () => { - if (sockets.includes(socket)) { - sockets.splice(sockets.indexOf(socket), 1) + if (socketId in socketMap) { + delete socketMap[socketId] } }) @@ -273,6 +278,7 @@ function makeSocketServer() { } command.sender = 'client' + command.senderSocketId = socketId command.senderNickname = nickname if (!validateCommand(command)) { @@ -286,25 +292,26 @@ function makeSocketServer() { switch (command.status) { case 'done-playing': { const doneSockets = donePlaying[command.queuePlayer] - if (doneSockets && !doneSockets.includes(socket)) { - doneSockets.push(socket) - if (doneSockets.length === sockets.length) { + if (doneSockets && !doneSockets.includes(socketId)) { + doneSockets.push(socketId) + if (doneSockets.length === Object.keys(socketMap).length) { // determine next track - for (const socket of sockets) { + for (const socket of Object.values(socketMap)) { // play next track } delete donePlaying[command.queuePlayer] } } + break } case 'ready-to-resume': { const readySockets = readyToResume[command.queuePlayer] - if (readySockets && !readySockets.includes(socket)) { - readySockets.push(socket) - if (readySockets.length === sockets.length) { + if (readySockets && !readySockets.includes(socketId)) { + readySockets.push(socketId) + if (readySockets.length === Object.keys(socketMap).length) { const QP = server.canonicalBackend.queuePlayers.find(QP => QP.id === command.queuePlayer) silenceEvents(QP, ['set-pause'], () => QP.setPause(false)) - for (const socket of sockets) { + for (const socket of Object.values(socketMap)) { socket.write(serializeCommandToData({ sender: 'server', code: 'set-pause', @@ -359,7 +366,7 @@ function makeSocketServer() { // Relay the command to client sockets besides the sender. - const otherSockets = sockets.filter(s => s !== socket) + const otherSockets = Object.values(socketMap).filter(s => s !== socket) for (const socket of otherSockets) { socket.write(serializeCommandToData(command) + '\n') @@ -374,6 +381,12 @@ function makeSocketServer() { } } + socket.write(serializeCommandToData({ + sender: 'server', + code: 'set-socket-id', + socketId + }) + '\n') + socket.write(serializeCommandToData({ sender: 'server', code: 'initialize-backend', @@ -392,6 +405,7 @@ function makeSocketClient() { const client = new EventEmitter() client.socket = new net.Socket() client.nickname = DEFAULT_NICKNAME + client.socketId = null // Will be received from server. client.sendCommand = function(command) { const data = serializeCommandToData(command) @@ -432,11 +446,16 @@ function attachBackendToSocketClient(backend, client, { // All actual logic for instances of the mtui backend interacting with each // other through commands lives here. - const partyGrouplike = {name: 'My Party Sources', isPartySources: true, items: []} + const partyGrouplike = { + name: `Party Sources - ${client.nickname}`, + isPartySources: true, + items: [] + } + + const partyGrouplikeMap = Object.create(null) backend.setAlwaysStartPaused(true) backend.setWaitWhenDonePlaying(true) - backend.loadPartyGrouplike(partyGrouplike) function logCommand(command) { const nickToMessage = nickname => `\x1b[32;1m${nickname}\x1b[0m` @@ -497,6 +516,8 @@ function attachBackendToSocketClient(backend, client, { actionmsg = `updated their nickname (from ${nickToMessage(command.oldNickname)})` senderNickname = command.nickname break + case 'set-socket-id': + return case 'set-pause': if (command.paused) { actionmsg = 'paused the player' @@ -548,6 +569,11 @@ function attachBackendToSocketClient(backend, client, { switch (command.sender) { case 'server': switch (command.code) { + case 'set-socket-id': + client.socketId = command.socketId + partyGrouplikeMap[command.socketId] = partyGrouplike + backend.loadPartyGrouplike(client.socketId, partyGrouplike) + return case 'initialize-backend': await restoreBackend(backend, command.backend) // TODO: does this need to be called here? @@ -559,7 +585,7 @@ function attachBackendToSocketClient(backend, client, { }) return } - // Again, no pause. Client commands can come from the server. + // Again, no break. Client commands can come from the server. case 'client': { let QP = ( command.queuePlayer && @@ -775,7 +801,7 @@ function attachBackendToSocketClient(backend, client, { if (partyGrouplike.items.every(x => x[originalSymbol] !== origItem)) { partyGrouplike.items.push(newItem) - backend.partyGrouplikeUpdated(partyGrouplike) + backend.partyGrouplikeUpdated(client.socketId, partyGrouplike) } }) } diff --git a/todo.txt b/todo.txt index fd024d7..b2068c9 100644 --- a/todo.txt +++ b/todo.txt @@ -581,3 +581,5 @@ TODO: Pressing next track (shift+N) on the last track should start the first track, if the queue is being looped. TODO: Tabber tab list should be accessible via tab (key). + +TODO: Show current index and number of tabs beside tabber tab list. diff --git a/ui.js b/ui.js index fb0916e..aaf776c 100644 --- a/ui.js +++ b/ui.js @@ -561,11 +561,11 @@ class AppElement extends FocusElement { this.log.newLogMessage(messageInfo) } - handleGotPartyGrouplike(partyGrouplike) { - this.newPartyTab(partyGrouplike) + handleGotPartyGrouplike(socketId, partyGrouplike) { + this.newPartyTab(socketId, partyGrouplike) } - handlePartyGrouplikeUpdated(partyGrouplike) { + handlePartyGrouplikeUpdated(socketId, partyGrouplike) { for (const grouplikeListing of this.tabber.tabberElements) { if (grouplikeListing.grouplike === partyGrouplike) { grouplikeListing.loadGrouplike(partyGrouplike, false) @@ -1579,7 +1579,7 @@ class AppElement extends FocusElement { }) } - newPartyTab(partyGrouplike) { + newPartyTab(socketId, partyGrouplike) { const listing = this.newGrouplikeListing() listing.loadGrouplike(partyGrouplike) } -- cgit 1.3.0-6-gf8a5