diff --git a/src/Channel.js b/src/Channel.js index e77970e..1ed5746 100644 --- a/src/Channel.js +++ b/src/Channel.js @@ -7,6 +7,7 @@ const RoomSettings = require('./RoomSettings.js'); const ftc = require('fancy-text-converter'); const Notification = require('./Notification'); const Color = require('./Color'); +const { getTimeColor } = require('./ColorEncoder.js'); class Channel extends EventEmitter { constructor(server, _id, settings) { @@ -41,8 +42,8 @@ class Channel extends EventEmitter { if (err) { return; } - - this.settings = set.settings; + + this.settings = RoomSettings.changeSettings(this.settings, true); this.chatmsgs = set.chat; this.connections.forEach(cl => { cl.sendArray([{ @@ -52,6 +53,13 @@ class Channel extends EventEmitter { }); this.setData(); }); + + if (this.isLobby(this._id)) { + this.colorInterval = setInterval(() => { + this.setDefaultLobbyColorBasedOnDate(); + }, 1000 * 60 * 5); + this.setDefaultLobbyColorBasedOnDate(); + } } setChatArray(arr) { @@ -63,16 +71,37 @@ class Channel extends EventEmitter { this.setData(); } + setDefaultLobbyColorBasedOnDate() { + let col = getTimeColor(); + let col2 = new Color(col.r - 0x40, col.g - 0x40, col.b - 0x40); + + this.settings.changeSettings({ + color: col.toHexa(), + color2: col2.toHexa() + }); + + for (let key in this.settings) { + this.server.lobbySettings[key] = this.settings[key]; + } + + this.emit('update'); + } + join(cl, set) { //this stuff is complicated let otheruser = this.connections.find((a) => a.user._id == cl.user._id) if (!otheruser) { + // we don't exist yet + // create id hash let participantId = createKeccakHash('keccak256').update((Math.random().toString() + cl.ip)).digest('hex').substr(0, 24); + // set id cl.user.id = participantId; cl.participantId = participantId; + + // init quotas (TODO pass type of room in?) cl.initParticipantQuotas(); - // if there are no users or the user with the crown entered the room, crown the user + // no users / already had crown? give crown if (((this.connections.length == 0 && Array.from(this.ppl.values()).length == 0) && this.isLobby(this._id) == false) || this.crown && (this.crown.userId == cl.user._id)) { // user owns the room // we need to switch the crown to them @@ -85,7 +114,8 @@ class Channel extends EventEmitter { //cl.quotas.a.setParams(Quota.PARAMS_A_NORMAL); if (this.isLobby(this._id) && this.settings.lobby !== true) { - this.settings.changeSettings(this.server.lobbySettings, 'user'); + // fix lobby setting + this.settings.changeSettings({lobby: true}); // this.settings.visible = true; // this.settings.crownsolo = false; // this.settings.lobby = true; @@ -110,21 +140,25 @@ class Channel extends EventEmitter { this.connections.push(cl); - if (cl.hidden !== true) { - this.sendArray([{ - color: this.ppl.get(cl.participantId).user.color, - id: this.ppl.get(cl.participantId).participantId, - m: "p", - name: this.ppl.get(cl.participantId).user.name, - x: this.ppl.get(cl.participantId).x || 200, - y: this.ppl.get(cl.participantId).y || 100, - _id: cl.user._id - }], cl, false) - } cl.sendArray([{ m: "c", c: this.chatmsgs.slice(-1 * 32) }]); + + // this.updateCh(cl, this.settings); + + if (!cl.user.hasFlag("hidden", true)) { + this.sendArray([{ + m: 'p', + _id: cl.user._id, + name: cl.user.name, + color: cl.user.color, + id: cl.participantId, + x: this.ppl.get(cl.participantId).x || 200, + y: this.ppl.get(cl.participantId).y || 100 + }], cl, false); + } + this.updateCh(cl, this.settings); } else { cl.user.id = otheruser.participantId; @@ -190,11 +224,11 @@ class Channel extends EventEmitter { } - updateCh(cl) { //update channel for all people in channel + updateCh(cl, set) { //update channel for all people in channel if (Array.from(this.ppl.values()).length <= 0) { setTimeout(() => { this.destroy(); - }, 1000); + }, 13000); } this.connections.forEach((usr) => { @@ -240,6 +274,7 @@ class Channel extends EventEmitter { destroy() { //destroy room if (this.destroyed) return; if (this.ppl.size > 0) return; + if (this._id == "lobby") return; this.destroyed = true; this._id; console.log(`Deleted room ${this._id}`); @@ -270,6 +305,7 @@ class Channel extends EventEmitter { [...this.ppl.values()].forEach(c => { if (cl) { if (c.hidden == true && c.user._id !== cl.user._id) { + // client is hidden and we are that client return; } else if (c.user._id == cl.user._id) { // let u = { @@ -519,11 +555,32 @@ class Channel extends EventEmitter { } playNote(cl, note) { - let vel = Math.round(cl.user.flags["volume"])/100 || undefined; + if (cl.user.hasFlag('mute', true)) { + return; + } + + if (cl.user.hasFlag('mute')) { + if (Array.isArray(cl.user.flags['mute'])) { + if (cl.user.flags['mute'].includes(this._id)) return; + } + } + + let vol; + + if (cl.user.hasFlag('volume')) { + vol = Math.round(cl.user.flags["volume"]) / 100; + } + - if (vel) { + if (typeof vol == 'number') { for (let no of note.n) { - no.v /= vel; + if (no.v) { + if (vol == 0) { + no.v = vol; + } else { + no.v *= vol; + } + } } } @@ -607,20 +664,28 @@ class Channel extends EventEmitter { bindEventListeners() { this.on("bye", participant => { this.remove(participant); - }) + }); this.on("m", msg => { let p = this.ppl.get(msg.p); if (!p) return; this.setCoords(p, msg.x, msg.y); - }) + }); - this.on("a", (participant, msg) => { - this.chat(participant, msg); - }) + this.on("a", (msg) => { + if (!msg.msg) return; + if (!msg.participant) return; + if (typeof msg.msg !== 'object') return; + if (typeof msg.participant !== 'object') return; + if (!msg.msg.m) return; + if (msg.msg.m !== 'a') return; + if (!msg.participant.name) return; + if (!msg.participant._id) return; + this.chat(msg.participant, msg.msg); + }); - this.on("update", (cl) => { - this.updateCh(cl); + this.on("update", (cl, set) => { + this.updateCh(cl, set); }); this.on("remove crown", () => { diff --git a/src/Client.js b/src/Client.js index 603d012..bc2a3f8 100644 --- a/src/Client.js +++ b/src/Client.js @@ -186,6 +186,16 @@ class Client extends EventEmitter { this.sendArray([data]); } + + /** + * + * @param {Channel} ch + * @param {Client} cl If this is present, only this client's user data will be sent(?) + */ + sendChannelUpdate(ch, cl) { + let msg = ch.fetchChannelData(this, cl); + this.sendArray([msg]); + } } module.exports = Client; diff --git a/src/ColorEncoder.js b/src/ColorEncoder.js index 72eaa34..368a8ff 100644 --- a/src/ColorEncoder.js +++ b/src/ColorEncoder.js @@ -1,3 +1,5 @@ +const Color = require("./Color"); + function hashCode(str) { // java String#hashCode var hash = 0; for (var i = 0; i < str.length; i++) { @@ -14,7 +16,71 @@ function intToRGB(i){ return "00000".substring(0, 6 - c.length) + c; } +/** + * Converts an HSL color value to RGB. Conversion formula + * adapted from http://en.wikipedia.org/wiki/HSL_color_space. + * Assumes h, s, and l are contained in the set [0, 1] and + * returns r, g, and b in the set [0, 255]. + * + * @param {number} h The hue + * @param {number} s The saturation + * @param {number} l The lightness + * @return {Array} The RGB representation + */ + function hslToRgb(h, s, l){ + var r, g, b; + + if(s == 0){ + r = g = b = l; // achromatic + }else{ + var hue2rgb = function hue2rgb(p, q, t){ + if(t < 0) t += 1; + if(t > 1) t -= 1; + if(t < 1/6) return p + (q - p) * 6 * t; + if(t < 1/2) return q; + if(t < 2/3) return p + (q - p) * (2/3 - t) * 6; + return p; + } + + var q = l < 0.5 ? l * (1 + s) : l + s - l * s; + var p = 2 * l - q; + r = hue2rgb(p, q, h + 1/3); + g = hue2rgb(p, q, h); + b = hue2rgb(p, q, h - 1/3); + } + + return [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255)]; +} + +function getTimeColor(currentDate = new Date()) { + // get day of year as a number from 1-365 + let newYearsDay = new Date(currentDate.getFullYear()); + let differenceInTime = (currentDate - newYearsDay) + ((newYearsDay.getTimezoneOffset() - currentDate.getTimezoneOffset()) * 60 * 1000); + let oneDayInMS = 1000 * 60 * 60 * 24; + let dayOfYear = Math.ceil(differenceInTime / oneDayInMS); + + // get hour + let hours = currentDate.getHours(); + let seconds = currentDate.getSeconds(); + + // get a hue based on time of day and day of year + let h = Math.floor((dayOfYear / 365) * 100) / 10000; + let s = (hours + 1) / (24 / 3); + // let s = 1; + let l = 0.1 + Math.floor(((hours / 60)) * 1000) / 1000; + if (l > 1) l = 1; + // let l = 0.5; + l = l / 2; + + // convert to rgb + let [r, g, b] = hslToRgb(h, s, l); + + let col = new Color(r, g, b); + return col; +} + module.exports = { hashCode, - intToRGB + intToRGB, + getTimeColor } diff --git a/src/Message.js b/src/Message.js index 2916296..a41ceb1 100644 --- a/src/Message.js +++ b/src/Message.js @@ -145,10 +145,11 @@ module.exports = (cl) => { } if (!msg.hasOwnProperty("set") || !msg.set) msg.set = new RoomSettings(cl.channel.settings, 'user'); cl.channel.settings.changeSettings(msg.set, admin); - cl.channel.updateCh(); + // cl.channel.updateCh(); + cl.channel.emit('update'); }); - cl.on("a", (msg, admin) => { + cl.on('a', (msg, admin) => { if (!(cl.channel && cl.participantId)) return; if (!msg.hasOwnProperty('message')) return; if (typeof(msg.message) !== 'string') return; @@ -395,4 +396,9 @@ module.exports = (cl) => { } } }); + + cl.on('restart', (msg, admin) => { + if (!admin) return; + cl.server.restart(msg.notification); + }); } diff --git a/src/Notification.js b/src/Notification.js index 881ea05..e8dba60 100644 --- a/src/Notification.js +++ b/src/Notification.js @@ -24,6 +24,7 @@ module.exports = class Notification { } break; case "room": + case "channel": for (let con of room.connections) { con.sendArray([msg]); } diff --git a/src/Server.js b/src/Server.js index dec3bb7..833fc0a 100644 --- a/src/Server.js +++ b/src/Server.js @@ -5,6 +5,7 @@ const http = require("http"); const fs = require('fs'); const RoomSettings = require('./RoomSettings'); const Logger = require("./Logger.js"); +const Notification = require('./Notification'); class Server extends EventEmitter { constructor(config) { @@ -89,7 +90,8 @@ class Server extends EventEmitter { "data", "channel message", "channel_flag", - "name" + "name", + "restart" ]; this.welcome_motd = config.motd || "You agree to read this message."; @@ -143,6 +145,23 @@ class Server extends EventEmitter { } return out; } + + restart(notif = { + m: "notification", + id: "server-restart", + title: "Notice", + text: "The server will restart in a few moments.", + target: "#piano", + duration: 20000, + class: "classic", + }) { + let n = new Notification(notif); + n.send("all", this.rooms.get('lobby')); + + setTimeout(() => { + process.exit(); + }, n.duration || 20000); + } } module.exports = Server;