From 0ee7f05213c19f19085e1347bb1335582294ea7c Mon Sep 17 00:00:00 2001 From: calzoneman Date: Sat, 2 Apr 2016 11:57:26 -0700 Subject: [PATCH] Make polls more efficient Instead of emitting frames to each individual socket, group them into socket.io rooms of people who can see hidden poll results and people who can't, then just do 2 broadcasts. --- package.json | 2 +- src/channel/channel.js | 4 +-- src/channel/playlist.js | 2 ++ src/channel/poll.js | 79 +++++++++++++++++++++++++---------------- src/channel/ranks.js | 1 + src/user.js | 3 ++ 6 files changed, 58 insertions(+), 33 deletions(-) diff --git a/package.json b/package.json index 0c597651..9ac92167 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "author": "Calvin Montgomery", "name": "CyTube", "description": "Online media synchronizer and chat", - "version": "3.14.6", + "version": "3.15.0", "repository": { "url": "http://github.com/calzoneman/sync" }, diff --git a/src/channel/channel.js b/src/channel/channel.js index c20e2cb9..2b8ca57a 100644 --- a/src/channel/channel.js +++ b/src/channel/channel.js @@ -646,12 +646,12 @@ Channel.prototype.handleReadLog = function (user) { }); }; -Channel.prototype._broadcast = function (msg, data, ns) { +Channel.prototype.broadcastToRoom = function (msg, data, ns) { sio.instance.in(ns).emit(msg, data); }; Channel.prototype.broadcastAll = function (msg, data) { - this._broadcast(msg, data, this.name); + this.broadcastToRoom(msg, data, this.name); }; Channel.prototype.packInfo = function (isAdmin) { diff --git a/src/channel/playlist.js b/src/channel/playlist.js index d138edab..82e7eb6e 100644 --- a/src/channel/playlist.js +++ b/src/channel/playlist.js @@ -740,6 +740,7 @@ PlaylistModule.prototype.handleAssignLeader = function (user, data) { this.leader = null; if (old.account.effectiveRank === 1.5) { old.account.effectiveRank = old.account.oldRank; + old.emit("effectiveRankChange", old.account.effectiveRank); old.socket.emit("rank", old.account.effectiveRank); } @@ -766,6 +767,7 @@ PlaylistModule.prototype.handleAssignLeader = function (user, data) { if (this.leader.account.effectiveRank < 1.5) { this.leader.account.oldRank = this.leader.account.effectiveRank; this.leader.account.effectiveRank = 1.5; + this.leader.emit("effectiveRankChange", 1.5); this.leader.socket.emit("rank", 1.5); } diff --git a/src/channel/poll.js b/src/channel/poll.js index 274b02b6..2257e082 100644 --- a/src/channel/poll.js +++ b/src/channel/poll.js @@ -12,10 +12,15 @@ const TYPE_VOTE = { option: "number" }; +const ROOM_VIEW_HIDDEN = ":viewHidden"; +const ROOM_NO_VIEW_HIDDEN = ":noViewHidden"; + function PollModule(channel) { ChannelModule.apply(this, arguments); this.poll = null; + this.roomViewHidden = this.channel.uniqueName + ROOM_VIEW_HIDDEN; + this.roomNoViewHidden = this.channel.uniqueName + ROOM_NO_VIEW_HIDDEN; if (this.channel.modules.chat) { this.channel.modules.chat.registerCommand("poll", this.handlePollCmd.bind(this, false)); this.channel.modules.chat.registerCommand("hpoll", this.handlePollCmd.bind(this, true)); @@ -59,20 +64,53 @@ PollModule.prototype.save = function (data) { }; PollModule.prototype.onUserPostJoin = function (user) { - this.sendPoll([user]); + this.sendPoll(user); user.socket.typecheckedOn("newPoll", TYPE_NEW_POLL, this.handleNewPoll.bind(this, user)); user.socket.typecheckedOn("vote", TYPE_VOTE, this.handleVote.bind(this, user)); user.socket.on("closePoll", this.handleClosePoll.bind(this, user)); + this.addUserToPollRoom(user); + const self = this; + user.on("effectiveRankChange", () => { + self.addUserToPollRoom(user); + }); +}; + +PollModule.prototype.addUserToPollRoom = function (user) { + const perms = this.channel.modules.permissions; + if (perms.canViewHiddenPoll(user)) { + user.socket.leave(this.roomNoViewHidden); + user.socket.join(this.roomViewHidden); + } else { + user.socket.leave(this.roomViewHidden); + user.socket.join(this.roomNoViewHidden); + } }; PollModule.prototype.onUserPart = function(user) { if (this.poll) { this.poll.unvote(user.realip); - this.sendPollUpdate(this.channel.users); + this.broadcastPoll(false); } }; -PollModule.prototype.sendPoll = function (users) { +PollModule.prototype.sendPoll = function (user) { + if (!this.poll) { + return; + } + + var perms = this.channel.modules.permissions; + + user.socket.emit("closePoll"); + if (perms.canViewHiddenPoll(user)) { + var unobscured = this.poll.packUpdate(true); + user.socket.emit("newPoll", unobscured); + } else { + var obscured = this.poll.packUpdate(false); + user.socket.emit("newPoll", obscured); + } +}; + +PollModule.prototype.broadcastPoll = function (isNewPoll) { if (!this.poll) { return; } @@ -81,32 +119,13 @@ PollModule.prototype.sendPoll = function (users) { var unobscured = this.poll.packUpdate(true); var perms = this.channel.modules.permissions; - users.forEach(function (u) { - u.socket.emit("closePoll"); - if (perms.canViewHiddenPoll(u)) { - u.socket.emit("newPoll", unobscured); - } else { - u.socket.emit("newPoll", obscured); - } - }); -}; - -PollModule.prototype.sendPollUpdate = function (users) { - if (!this.poll) { - return; + const event = isNewPoll ? "newPoll" : "updatePoll"; + if (isNewPoll) { + this.channel.broadcastAll("closePoll"); } - var obscured = this.poll.packUpdate(false); - var unobscured = this.poll.packUpdate(true); - var perms = this.channel.modules.permissions; - - users.forEach(function (u) { - if (perms.canViewHiddenPoll(u)) { - u.socket.emit("updatePoll", unobscured); - } else { - u.socket.emit("updatePoll", obscured); - } - }); + this.channel.broadcastToRoom(event, unobscured, this.roomViewHidden); + this.channel.broadcastToRoom(event, obscured, this.roomNoViewHidden); }; PollModule.prototype.handleNewPoll = function (user, data) { @@ -132,7 +151,7 @@ PollModule.prototype.handleNewPoll = function (user, data) { } this.poll = poll; - this.sendPoll(this.channel.users); + this.broadcastPoll(true); this.channel.logger.log("[poll] " + user.getName() + " opened poll: '" + poll.title + "'"); }; @@ -143,7 +162,7 @@ PollModule.prototype.handleVote = function (user, data) { if (this.poll) { this.poll.vote(user.realip, data.option); - this.sendPollUpdate(this.channel.users); + this.broadcastPoll(false); } }; @@ -179,7 +198,7 @@ PollModule.prototype.handlePollCmd = function (obscured, user, msg, meta) { var title = args.shift(); var poll = new Poll(user.getName(), title, args, obscured); this.poll = poll; - this.sendPoll(this.channel.users); + this.broadcastPoll(true); this.channel.logger.log("[poll] " + user.getName() + " opened poll: '" + poll.title + "'"); }; diff --git a/src/channel/ranks.js b/src/channel/ranks.js index 2dbfdb02..b841516b 100644 --- a/src/channel/ranks.js +++ b/src/channel/ranks.js @@ -121,6 +121,7 @@ RankModule.prototype.handleRankChange = function (user, data) { receiver.account.channelRank = rank; receiver.account.effectiveRank = Math.max(receiver.account.globalRank, rank); + receiver.emit("effectiveRankChange", receiver.account.effectiveRank); receiver.socket.emit("rank", receiver.account.effectiveRank); this.channel.logger.log("[mod] " + user.getName() + " set " + name + "'s rank " + "to " + rank); diff --git a/src/user.js b/src/user.js index 6ae5dae6..b779a672 100644 --- a/src/user.js +++ b/src/user.js @@ -429,6 +429,9 @@ User.prototype.refreshAccount = function (opts, cb) { } } self.account = account; + if (account.effectiveRank !== old.effectiveRank) { + self.emit("effectiveRankChange", self.account.effectiveRank); + } } cb(err, account); });