Add everything
This commit is contained in:
Wolfy 2020-04-07 03:55:16 -04:00
parent 9aa2acfd64
commit 280c0d67c5
16 changed files with 1263 additions and 0 deletions

51
Quotas.js Normal file
View File

@ -0,0 +1,51 @@
module.exports = Object.seal({
"note": {
"lobby": {
"allowance": 200,
"max": 600,
"maxHistLen": 3
},
"normal": {
"allowance": 400,
"max": 1200,
"maxHistLen": 3
},
"insane": {
"allowance": 600,
"max": 1800,
"maxHistLen": 3
}
},
"chat": {
"lobby": {
"amount": 4,
"time": 4000
},
"normal": {
"amount": 4,
"time": 4000
},
"insane": {
"amount": 10,
"time": 4000
}
},
"chown": {
"amount": 10,
"time": 5000
},
"name": {
"amount": 30,
"time": 30 * 60000
},
"room": {
"time": 500
},
"cursor": {
"time": 16
},
"kickban": {
"amount": 2,
"time": 1000
}
})

2
README.md Normal file
View File

@ -0,0 +1,2 @@
# mpp-server
Attempt at making a MPP Server.

3
banned.json Normal file
View File

@ -0,0 +1,3 @@
[
]

10
config.js Normal file
View File

@ -0,0 +1,10 @@
module.exports = Object.seal({
"port": "8080",
"motd": "You agree to read this message.",
"_id_PrivateKey": "boppity",
"defaultUsername": "Anonymous",
"defaultRoomColor": "#3b5054",
"defaultLobbyColor": "#19b4b9",
"defaultLobbyColor2": "#801014",
"adminpass": "27PP6YLTxg0b1P2B8eGSOki1"
})

27
index.js Normal file
View File

@ -0,0 +1,27 @@
//call new Server
global.WebSocket = require('ws');
global.EventEmitter = require('events').EventEmitter;
global.fs = require('fs');
global.createKeccakHash = require('keccak');
const AsyncConsole = require('asyncconsole')
global.isString = function(a){
return typeof a === 'string';
}
global.isBool = function(a){
return typeof a === 'boolean';
}
global.isObj = function(a){
return typeof a === "object" && !Array.isArray(a) && a !== null;
}
let Server = require("./src/Server");
let config = require('./config');
global.SERVER = new Server(config);
let console = process.platform == 'win32' ? new AsyncConsole("", input => {
try {
console.log(JSON.stringify(eval(input)));
} catch(e) {
console.log(e.toString());
}
}) : {};

32
package.json Normal file
View File

@ -0,0 +1,32 @@
{
"name": "mpp-server-master",
"version": "1.0.0",
"description": "Attempt at making a MPP Server.",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "git+https://github.com/BopItFreak/mpp-server.git"
},
"keywords": [
"mpp",
"server",
"multiplayerpiano"
],
"author": "BopItFreak",
"license": "ISC",
"bugs": {
"url": "https://github.com/BopItFreak/mpp-server/issues"
},
"homepage": "https://github.com/BopItFreak/mpp-server#readme",
"dependencies": {
"asyncconsole": "^1.3.9",
"events": "^3.1.0",
"keccak": "^2.1.0",
"node-json-color-stringify": "^1.1.0",
"ws": "^7.2.3"
},
"devDependencies": {}
}

121
src/Client.js Normal file
View File

@ -0,0 +1,121 @@
const quotas = require('../Quotas');
const RateLimit = require('./RateLimit.js').RateLimit;
const RateLimitChain = require('./RateLimit.js').RateLimitChain;
const Room = require("./Room.js");
require('node-json-color-stringify');
class Client extends EventEmitter {
constructor(ws, req, server) {
super();
EventEmitter.call(this);
this.user;
this.connectionid = server.connectionid;
this.server = server;
this.participantId;
this.channel;
this.ws = ws;
this.req = req;
this.ip = (req.connection.remoteAddress).replace("::ffff:", "");
this.destroied = false;
this.bindEventListeners();
this.quotas = {
//note: new limiter(2000, { allowance:3000, max:24000, maxHistLen:3}),
chat: {
lobby: new RateLimitChain(quotas.chat.lobby.amount, quotas.chat.lobby.time),
normal: new RateLimitChain(quotas.chat.normal.amount, quotas.chat.normal.time),
insane: new RateLimitChain(quotas.chat.insane.amount, quotas.chat.insane.time)
},
name: new RateLimitChain(quotas.name.amount, quotas.name.time),
room: new RateLimit(quotas.room.time),
chown: new RateLimitChain(quotas.chown.amount, quotas.chown.time),
cursor: new RateLimit(quotas.cursor.time),
kickban: new RateLimitChain(quotas.kickban.amount, quotas.kickban.time),
}
require('./Message.js')(this);
}
isConnected() {
return this.ws && this.ws.readyState === WebSocket.OPEN;
}
isConnecting() {
return this.ws && this.ws.readyState === WebSocket.CONNECTING;
}
setChannel(_id, settings) {
if (this.channel && this.channel._id == _id) return;
if (this.server.rooms.get(_id)) {
let room = this.server.rooms.get(_id);
let userbanned = room.bans.get(this.user._id);
if (userbanned && (Date.now() - userbanned.bannedtime >= userbanned.msbanned)) {
room.bans.delete(userbanned.user._id);
userbanned = undefined;
}
if (userbanned) {
console.log(Date.now() - userbanned.bannedtime)
room.Notification(this.user._id,
"Notice",
`Currently banned from \"${_id}\" for ${Math.ceil(Math.floor((userbanned.msbanned - (Date.now() - userbanned.bannedtime)) / 1000) / 60)} minutes.`,
7000,
"",
"#room",
"short"
);
return;
}
let channel = this.channel;
if (channel) this.channel.emit("bye", this);
if (channel) this.channel.updateCh();
this.channel = this.server.rooms.get(_id);
this.channel.join(this);
} else {
let room = new Room(this.server, _id, settings);
this.server.rooms.set(_id, room);
if (this.channel) this.channel.emit("bye", this);
this.channel = this.server.rooms.get(_id);
this.channel.join(this);
}
}
sendArray(arr) {
if (this.isConnected()) {
//console.log(`SEND: `, JSON.colorStringify(arr));
this.ws.send(JSON.stringify(arr));
}
}
destroy() {
this.ws.close();
if (this.channel) {
this.channel.emit("bye", this)
}
this.user;
this.participantId;
this.channel;
this.server.roomlisteners.delete(this.connectionid);
this.connectionid;
this.server.connections.delete(this.connectionid);
this.destroied = true;
console.log(`Removed Connection ${this.connectionid}.`);
}
bindEventListeners() {
this.ws.on("message", (evt, admin) => {
try {
let transmission = JSON.parse(evt);
for (let msg of transmission) {
if (!msg.hasOwnProperty("m")) return;
if (!this.server.legit_m.includes(msg.m)) return;
this.emit(msg.m, msg, !!admin);
//console.log(`RECIEVE: `, JSON.colorStringify(msg));
}
} catch (e) {
console.log(e)
this.destroy();
}
});
this.ws.on("close", () => {
if (!this.destroied)
this.destroy();
});
this.ws.addEventListener("error", (err) => {
console.error(err);
if (!this.destroied)
this.destroy();
});
}
}
module.exports = Client;

16
src/ColorEncoder.js Normal file
View File

@ -0,0 +1,16 @@
function hashCode(str) { // java String#hashCode
var hash = 0;
for (var i = 0; i < str.length; i++) {
hash = str.charCodeAt(i) + ((hash << 5) - hash);
}
return hash;
}
function intToRGB(i){
var c = (i & 0x00FFFFFF)
.toString(16)
.toUpperCase();
return "00000".substring(0, 6 - c.length) + c;
}
module.exports = {hashCode, intToRGB};

254
src/Message.js Normal file
View File

@ -0,0 +1,254 @@
const config = require('./db/config');
const quotas = config.quotas;
const User = require("./User.js");
module.exports = (cl) => {
cl.once("hi", () => {
let user = new User(cl);
user.getUserData().then((data) => {
let msg = {};
msg.m = "hi";
msg.motd = cl.server.welcome_motd;
msg.t = Date.now();
msg.u = data;
msg.v = "Beta";
cl.sendArray([msg])
cl.user = data;
})
})
cl.on("t", msg => {
if (msg.hasOwnProperty("e") && !isNaN(msg.e))
cl.sendArray([{
m: "t",
t: Date.now(),
e: msg.e
}])
})
cl.on("ch", msg => {
if (!cl.quotas.room.attempt()) return;
if (!msg.hasOwnProperty("set") || !msg.set) msg.set = {};
if (msg.hasOwnProperty("_id") && typeof msg._id == "string") {
if (msg._id.length > 512) return;
cl.setChannel(msg._id, msg.set);
if (cl.channel.isLobby(cl.channel._id)) {
cl.channel.sendNotequota(quotas.note.lobby.allowance, quotas.note.lobby.max, quotas.note.lobby.maxHistLen);
} else {
if (!(cl.user._id == cl.channel.crown.userId)) {
cl.channel.sendNotequota(quotas.note.normal.allowance, quotas.note.normal.max, quotas.note.normal.maxHistLen);
} else {
cl.channel.sendNotequota(quotas.note.insane.allowance, quotas.note.insane.max, quotas.note.insane.maxHistLen);
}
}
}
})
cl.on("m", msg => {
if (!cl.quotas.cursor.attempt()) return;
if (!(cl.channel && cl.participantId)) return;
if (!msg.hasOwnProperty("x")) msg.x = null;
if (!msg.hasOwnProperty("y")) msg.y = null;
if (parseInt(msg.x) == NaN) msg.x = null;
if (parseInt(msg.y) == NaN) msg.y = null;
cl.channel.emit("m", cl, msg.x, msg.y)
})
cl.on("chown", msg => {
if (!cl.quotas.chown.attempt()) return;
if (!(cl.channel && cl.participantId)) return;
//console.log((Date.now() - cl.channel.crown.time))
//console.log(!(cl.channel.crown.userId != cl.user._id), !((Date.now() - cl.channel.crown.time) > 15000));
if (!(cl.channel.crown.userId == cl.user._id) && !((Date.now() - cl.channel.crown.time) > 15000)) return;
if (msg.hasOwnProperty("id")) {
// console.log(cl.channel.crown)
if (cl.user._id == cl.channel.crown.userId || cl.channel.crowndropped)
cl.channel.chown(msg.id);
} else {
if (cl.user._id == cl.channel.crown.userId || cl.channel.crowndropped)
cl.channel.chown();
}
})
cl.on("chset", msg => {
if (!(cl.channel && cl.participantId)) return;
if (!(cl.user._id == cl.channel.crown.userId)) return;
if (!msg.hasOwnProperty("set") || !msg.set) msg.set = cl.channel.verifySet(cl.channel._id,{});
cl.channel.settings = msg.set;
cl.channel.updateCh();
})
cl.on("a", msg => {
if (cl.channel.isLobby(cl.channel._id)) {
if (!cl.quotas.chat.lobby.attempt()) return;
} else {
if (!(cl.user._id == cl.channel.crown.userId)) {
if (!cl.quotas.chat.normal.attempt()) return;
} else {
if (!cl.quotas.chat.insane.attempt()) return;
}
}
if (!(cl.channel && cl.participantId)) return;
if (!msg.hasOwnProperty('message')) return;
if (cl.channel.settings.chat) {
cl.channel.emit('a', cl, msg);
}
})
cl.on('n', msg => {
if (!(cl.channel && cl.participantId)) return;
if (!msg.hasOwnProperty('t') || !msg.hasOwnProperty('n')) return;
if (typeof msg.t != 'number' || typeof msg.n != 'object') return;
if (cl.channel.settings.crownsolo) {
if ((cl.channel.crown.userId == cl.user._id) && !cl.channel.crowndropped) {
cl.channel.playNote(cl, msg);
}
} else {
cl.channel.playNote(cl, msg);
}
})
cl.on('+ls', msg => {
if (!(cl.channel && cl.participantId)) return;
cl.server.roomlisteners.set(cl.connectionid, cl);
let rooms = [];
for (let room of Array.from(cl.server.rooms.values())) {
let data = room.fetchData().ch;
if (room.bans.get(cl.user._id)) {
data.banned = true;
}
if (room.settings.visible) rooms.push(data);
}
cl.sendArray([{
"m": "ls",
"c": true,
"u": rooms
}])
})
cl.on('-ls', msg => {
if (!(cl.channel && cl.participantId)) return;
cl.server.roomlisteners.delete(cl.connectionid);
})
cl.on("userset", msg => {
if (!cl.quotas.name.attempt()) return;
if (!(cl.channel && cl.participantId)) return;
if (!msg.hasOwnProperty("set") || !msg.set) msg.set = {};
if (msg.set.hasOwnProperty('name') && typeof msg.set.name == "string") {
if (msg.set.name.length > 40) return;
cl.user.name = msg.set.name;
let user = new User(cl);
user.getUserData().then((usr) => {
let dbentry = user.userdb.get(cl.user._id);
if (!dbentry) return;
dbentry.name = msg.set.name;
user.updatedb();
cl.server.rooms.forEach((room) => {
room.updateParticipant(cl.participantId, {
name: msg.set.name
});
})
})
}
})
cl.on('kickban', msg => {
if (!cl.quotas.kickban.attempt()) return;
if (!(cl.channel && cl.participantId)) return;
if (!(cl.user._id == cl.channel.crown.userId)) return;
if (msg.hasOwnProperty('_id') && typeof msg._id == "string") {
let _id = msg._id;
let ms = msg.ms || 0;
cl.channel.kickban(_id, ms);
}
})
cl.on("bye", msg => {
cl.destroy();
})
cl.on("admin message", msg => {
if (!(cl.channel && cl.participantId)) return;
if (!msg.hasOwnProperty('password') || !msg.hasOwnProperty('msg')) return;
if (typeof msg.msg != 'object') return;
if (msg.password !== cl.server.adminpass) return;
cl.ws.emit("message", JSON.stringify([msg.msg]), true);
})
//admin only stuff
/*
List of admin only stuff
1. admin_color
2. admin_noteColor
3. admin_chown
4. admin_kickban
5. admin_chset
*/
cl.on('admin_color', (msg, admin) => {
if (!admin) return;
if (typeof cl.channel.verifyColor(msg.color) != 'string') return;
if (!msg.hasOwnProperty('id') && !msg.hasOwnProperty('_id')) return;
cl.server.connections.forEach((usr) => {
if ((usr.channel && usr.participantId && usr.user) && (usr.user._id == msg._id || (usr.participantId == msg.id))) {
let user = new User(usr);
user.cl.user.color = msg.color;
user.getUserData().then((uSr) => {
if (!uSr._id) return;
let dbentry = user.userdb.get(uSr._id);
if (!dbentry) return;
dbentry.color = msg.color;
dbentry.noteColor = msg.color;
//user.updatedb();
cl.server.rooms.forEach((room) => {
room.updateParticipant(usr.participantId, {
color: msg.color,
noteColor: msg.color
});
})
})
}
})
})
cl.on('admin_noteColor', (msg, admin) => {
if (!admin) return;
if (typeof cl.channel.verifyColor(msg.color) != 'string') return;
if (!msg.hasOwnProperty('id') && !msg.hasOwnProperty('_id')) return;
cl.server.connections.forEach((usr) => {
if ((usr.channel && usr.participantId && usr.user) && (usr.user._id == msg._id || (usr.participantId == msg.id))) {
let user = new User(usr);
//user.getUserData().then((uSr) => {
//if (!uSr._id) return;
//let dbentry = user.userdb.get(uSr._id);
//if (!dbentry) return;
//dbentry.color = msg.color;
//user.updatedb();
cl.server.rooms.forEach((room) => {
room.updateParticipant(usr.participantId, {
noteColor: msg.color
});
})
//})
}
})
})
cl.on("admin_chown", (msg, admin) => {
if (!admin) return;
if (msg.hasOwnProperty("id")) {
cl.channel.chown(msg.id);
console.log(msg.id);
} else {
cl.channel.chown();
}
})
cl.on('admin_kickban', (msg, admin) => {
if (!admin) return;
if (msg.hasOwnProperty('_id') && typeof msg._id == "string") {
let _id = msg._id;
let ms = msg.ms || 0;
cl.channel.kickban(_id, ms);
}
})
cl.on("admin_chset", (msg, admin) => {
if (!admin) return;
if (!msg.hasOwnProperty("set") || !msg.set) msg.set = cl.channel.verifySet(cl.channel._id,{});
cl.channel.settings = msg.set;
cl.channel.updateCh();
})
cl.on("admin_notification", (msg, admin) => {
if (!admin) return;
cl.channel.Notification(msg.content);
console.log(msg.content);
})
}

58
src/Quota.js Normal file
View File

@ -0,0 +1,58 @@
function RateLimit(a,b){
this.a = b.a || 1;
this.m = b.m || 10;
this.mh = b.mh || 3;
this.setParams(a,{a:this.a,m:this.m,mh:this.mh});
this.resetPoints();
if(a !== null){
var self = this;
this.giveInt = setInterval(()=>{self.give()},a);
};
};
RateLimit.prototype.setParams = function(a,b){
var a = b.a || this.a || 1;
var m = b.m || this.m || 5;
var mh = b.mh || this.mh || 3;
clearInterval(this.giveInt);
this.giveInt = undefined;
if(a !== this.a || m !== this.m || mh !== this.mh){
this.a = a;
this.m = m;
this.mh = mh;
this.resetPoints();
if(a !== null){
var self = this;
this.giveInt = setInterval(()=>{self.give()},a);
};
return true;
};
return false;
};
RateLimit.prototype.resetPoints = function(){
this.points = this.m;
this.history = [];
for(var i=0; i<this.mh; i++) this.history.unshift(this.points);
};
RateLimit.prototype.give = function(){
this.history.unshift(this.points);
this.history.length = this.mh;
if(this.points < this.m){
this.points += this.a;
if(this.points > this.m) this.points = this.m;
};
};
RateLimit.prototype.spend = function(needed){
var sum = 0;
for(var i in this.history){
sum += this.history[i];
};
if(sum <= 0) needed *= this.a;
if(this.points < needed){
return false;
}else{
this.points -= needed;
return true;
};
};
module.exports = RateLimit;

40
src/Ratelimit.js Normal file
View File

@ -0,0 +1,40 @@
var RateLimit = function(interval_ms) {
this._interval_ms = interval_ms || 0; // (0 means no limit)
this._after = 0;
};
RateLimit.prototype.attempt = function(time) {
var time = time || Date.now();
if(time < this._after) return false;
this._after = time + this._interval_ms;
return true;
};
RateLimit.prototype.setInterval = function(interval_ms) {
this._after += interval_ms - this._interval_ms;
this._interval_ms = interval_ms;
};
var RateLimitChain = function(num, interval_ms) {
this.setNumAndInterval(num, interval_ms);
};
RateLimitChain.prototype.attempt = function(time) {
var time = time || Date.now();
for(var i = 0; i < this._chain.length; i++) {
if(this._chain[i].attempt(time)) return true;
}
return false;
};
RateLimitChain.prototype.setNumAndInterval = function(num, interval_ms) {
this._chain = [];
for(var i = 0; i < num; i++) {
this._chain.push(new RateLimit(interval_ms));
}
};
var exports = typeof module !== "undefined" ? module.exports : this;
exports.RateLimit = RateLimit;
exports.RateLimitChain = RateLimitChain;

432
src/Room.js Normal file
View File

@ -0,0 +1,432 @@
//array of rooms
//room class
//room deleter
//databases in Map
class Room extends EventEmitter {
constructor(server, _id, settings) {
super();
EventEmitter.call(this);
this._id = _id;
this.server = server;
this.crown = null;
this.crowndropped = false;
this.settings = this.verifySet(this._id,{set:settings});
this.chatmsgs = [];
this.ppl = new Map();
this.connections = [];
this.bindEventListeners();
this.server.rooms.set(_id, this);
this.bans = new Map();
}
join(cl) { //this stuff is complicated
let otheruser = this.connections.find((a) => a.user._id == cl.user._id)
if (!otheruser) {
let participantId = createKeccakHash('keccak256').update((Math.random().toString() + cl.ip)).digest('hex').substr(0, 24);
cl.user.id = participantId;
cl.participantId = participantId;
if (((this.connections.length == 0 && Array.from(this.ppl.values()).length == 0) && !this.isLobby(this._id)) || this.crown && (this.crown.userId == cl.user._id)) { //user that created the room, give them the crown.
this.crown = {
participantId: cl.participantId,
userId: cl.user._id,
time: Date.now(),
startPos: {
x: 50,
y: 50
},
endPos: {
x: this.getCrownX(),
y: this.getCrownY()
}
}
this.crowndropped = false;
}
this.ppl.set(participantId, cl);
this.connections.push(cl);
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);
} else {
cl.user.id = otheruser.participantId;
cl.participantId = otheruser.participantId;
this.connections.push(cl);
cl.sendArray([{
m: "c",
c: this.chatmsgs.slice(-1 * 32)
}])
this.updateCh(cl);
}
}
remove(p) { //this is complicated too
let otheruser = this.connections.filter((a) => a.user._id == p.user._id);
if (!(otheruser.length > 1)) {
this.ppl.delete(p.participantId);
this.connections.splice(this.connections.findIndex((a) => a.connectionid == p.connectionid), 1);
console.log(`Deleted client ${p.user.id}`);
this.sendArray([{
m: "bye",
p: p.participantId
}], p, false);
if (this.crown)
if (this.crown.userId == p.user._id && !this.crowndropped) {
this.chown();
}
this.updateCh();
} else {
this.connections.splice(this.connections.findIndex((a) => a.connectionid == p.connectionid), 1);
}
}
updateCh(cl) { //update channel for all people in channel
if (Array.from(this.ppl.values()).length <= 0) this.destroy();
this.connections.forEach((usr) => {
this.server.connections.get(usr.connectionid).sendArray([this.fetchData(usr, cl)])
})
this.server.updateRoom(this.fetchData());
}
updateParticipant(pid, options) {
let p = this.ppl.get(pid);
if (!p) return;
options.name ? this.ppl.get(pid).user.name = options.name : {};
options._id ? this.ppl.get(pid).user._id = options._id : {};
options.color ? this.ppl.get(pid).user.color = options.color : {};
options.noteColor ? this.ppl.get(pid).user.noteColor = options.noteColor : {};
this.connections.filter((ofo) => ofo.participantId == p.participantId).forEach((usr) => {
options.name ? usr.user.name = options.name : {};
options._id ? usr.user._id = options._id : {};
options.color ? usr.user.color = options.color : {};
options.noteColor ? usr.user.noteColor = options.noteColor : {};
})
this.sendArray([{
color: p.user.color,
noteColor: p.user.noteColor,
//noteColor: "#000",
id: p.participantId,
m: "p",
name: p.user.name,
x: p.x || 200,
y: p.y || 100,
_id: p.user._id
}])
}
destroy() { //destroy room
this._id;
console.log(`Deleted room ${this._id}`);
this.settings = {};
this.ppl;
this.connnections;
this.chatmsgs;
this.server.rooms.delete(this._id);
}
sendArray(arr, not, onlythisparticipant) {
this.connections.forEach((usr) => {
if (!not || (usr.participantId != not.participantId && !onlythisparticipant) || (usr.connectionid != not.connectionid && onlythisparticipant)) {
try {
this.server.connections.get(usr.connectionid).sendArray(arr)
} catch (e) {
console.log(e);
}
}
})
}
fetchData(usr, cl) {
let chppl = [];
[...this.ppl.values()].forEach((a) => {
chppl.push(a.user);
})
let data = {
m: "ch",
p: "ofo",
ch: {
count: chppl.length,
crown: this.crown,
settings: this.settings,
_id: this._id
},
ppl: chppl
}
if (cl) {
if (usr.connectionid == cl.connectionid) {
data.p = cl.participantId;
} else {
delete data.p;
}
} else {
delete data.p;
}
if (data.ch.crown == null) {
delete data.ch.crown;
} else {
}
return data;
}
verifyColor(strColor){
var test2 = /^#[0-9A-F]{6}$/i.test(strColor);
if(test2 == true){
return strColor;
} else{
return false;
}
}
isLobby(_id) {
if (_id.startsWith("lobby")) {
let lobbynum = _id.split("lobby")[1];
if (_id == "lobby") {
return true;
}
if (!(parseInt(lobbynum).toString() == lobbynum)) return false;
for (let i in lobbynum) {
if (parseInt(lobbynum[i]) >= 0) {
if (parseInt(i) + 1 == lobbynum.length) return true;
} else {
return false;
}
}
} else if (_id.startsWith("test/")) {
if (_id == "test/") {
return false;
} else {
return true;
}
} else {
return false;
}
}
getCrownY() {
return 50 - 30;
}
getCrownX() {
return 50;
}
chown(id) {
let prsn = this.ppl.get(id);
if (prsn) {
this.crown = {
participantId: prsn.participantId,
userId: prsn.user._id,
time: Date.now(),
startPos: {
x: 50,
y: 50
},
endPos: {
x: this.getCrownX(),
y: this.getCrownY()
},
}
this.crowndropped = false;
} else {
this.crown = {
userId: this.crown.userId,
time: Date.now(),
startPos: {
x: 50,
y: 50
},
endPos: {
x: this.getCrownX(),
y: this.getCrownY()
}
}
this.crowndropped = true;
}
this.updateCh();
}
setCords(p, x, y) {
if (p.participantId && this.ppl.get(p.participantId)) {
x ? this.ppl.get(p.participantId).x = x : {};
y ? this.ppl.get(p.participantId).y = y : {};
this.sendArray([{
m: "m",
id: p.participantId,
x: this.ppl.get(p.participantId).x,
y: this.ppl.get(p.participantId).y
}], p, false);
}
}
chat(p, msg) {
if (msg.message.length > 512) return;
let filter = ["AMIGHTYWIND"];
let regexp = new RegExp("\\b(" + filter.join("|") + ")\\b", "i");
if (regexp.test(msg.message)) return;
let prsn = this.ppl.get(p.participantId);
if (prsn) {
let message = {};
message.m = "a";
message.a = msg.message;
message.p = {
color: p.user.color,
id: p.participantId,
name: p.user.name,
_id: p.user._id
};
message.t = Date.now();
this.sendArray([message]);
this.chatmsgs.push(message);
}
}
playNote(cl, note) {
this.sendArray([{
m: "n",
n: note.n,
p: cl.participantId,
t: note.t
}], cl, true);
}
sendNotequota(allowance = 200, max = 600, maxHistLen = 3){
this.sendArray([{
m: 'nq',
allowance: allowance,
max: max,
maxHistLen: maxHistLen
}])
}
kickban(_id, ms) {
ms = parseInt(ms);
if (ms >= (1000 * 60 * 60 - 500)) return;
if (ms < 0) return;
ms = Math.round(ms / 1000) * 1000;
let user = this.connections.find((usr) => usr.user._id == _id);
if (!user) return;
let asd = true;
let tonc = true;
let pthatbanned = this.ppl.get(this.crown.participantId);
this.connections.filter((usr) => usr.participantId == user.participantId).forEach((u) => {
user.bantime = Math.floor(Math.floor(ms / 1000) / 60);
user.bannedtime = Date.now();
user.msbanned = ms;
this.bans.set(user.user._id, user);
if (this.crown && (this.crown.userId)) {
u.setChannel("test/awkward", {});
if (asd)
this.Notification(user.user._id,
"Notice",
`Banned from \"${this._id}\" for ${Math.floor(Math.floor(ms / 1000) / 60)} minutes.`,
"",
7000,
"#room",
"short"
)
if (asd)
this.Notification("room",
"Notice",
`${pthatbanned.user.name} banned ${user.user.name} from the channel for ${Math.floor(Math.floor(ms / 1000) / 60)} minutes.`,
"",
7000,
"#room",
"short"
)
if (this.crown && (this.crown.userId == _id) && tonc) {
this.Notification("room",
"Certificate of Award",
`Let it be known that ${user.user.name} kickbanned him/her self.`,
"",
7000,
"#room"
);
tonc = false;
}
}
})
}
Notification(who, title, text, html, duration, target, klass, id) {
let obj = {
m: "notification",
title: title,
text: text,
html: html,
target: target,
duration: duration,
class: klass,
id: id
};
if (!id) delete obj.id;
if (!title) delete obj.title;
if (!text) delete obj.text;
if (!html) delete obj.html;
if (!target) delete obj.target;
if (!duration) delete obj.duration;
if (!klass) delete obj.class;
switch (who) {
case "all": {
for (let con of Array.from(this.server.connections.values())) {
con.sendArray([obj]);
}
break;
}
case "room": {
for (let con of this.connections) {
con.sendArray([obj]);
}
break;
}
default: {
Array.from(this.server.connections.values()).filter((usr) => usr.user._id == who).forEach((p) => {
p.sendArray([obj]);
});
}
}
}
bindEventListeners() {
this.on("bye", participant => {
this.remove(participant);
})
this.on("m", (participant, x, y) => {
this.setCords(participant, x, y);
})
this.on("a", (participant, msg) => {
this.chat(participant, msg);
})
}
verifySet(_id,msg){
if(!isObj(msg.set)) msg.set = {visible:true,color:this.server.defaultRoomColor,chat:true,crownsolo:false};
if(isBool(msg.set.lobby)){
if(!this.isLobby(_id)) delete msg.set.lobby; // keep it nice and clean
}else{
if(this.isLobby(_id)) msg.set = {visible:true,color:this.server.defaultLobbyColor,color2:this.server.defaultLobbyColor2,chat:true,crownsolo:false,lobby:true};
}
if(!isBool(msg.set.visible)){
if(msg.set.visible == undefined) msg.set.visible = (!isObj(this.settings) ? true : this.settings.visible);
else msg.set.visible = true;
};
if(!isBool(msg.set.chat)){
if(msg.set.chat == undefined) msg.set.chat = (!isObj(this.settings) ? true : this.settings.chat);
else msg.set.chat = true;
};
if(!isBool(msg.set.crownsolo)){
if(msg.set.crownsolo == undefined) msg.set.crownsolo = (!isObj(this.settings) ? false : this.settings.crownsolo);
else msg.set.crownsolo = false;
};
if(!isString(msg.set.color) || !/^#[0-9a-f]{6}$/i.test(msg.set.color)) msg.set.color = (!isObj(this.settings) ? this.server.defaultRoomColor : this.settings.color);
if(isString(msg.set.color2)){
if(!/^#[0-9a-f]{6}$/i.test(msg.set.color2)){
if(this.settings){
if(this.settings.color2) msg.set.color2 = this.settings.color2;
else delete msg.set.color2; // keep it nice and clean
}
}
};
return msg.set;
}
}
module.exports = Room;

45
src/Server.js Normal file
View File

@ -0,0 +1,45 @@
const Client = require("./Client.js");
const banned = require('../banned.json');
class Server extends EventEmitter {
constructor(config) {
super();
EventEmitter.call(this);
this.wss = new WebSocket.Server({
port: config.port,
backlog: 100,
verifyClient: (info) => {
//if (banned.includes(info.req.headers['x-forwarded-for'].split(",")[0].replace('::ffff:', ''))) return false;
if (banned.includes((info.req.connection.remoteAddress).replace("::ffff:", ""))) return false;
return true;
}
});
this.connectionid = 0;
this.connections = new Map();
this.roomlisteners = new Map();
this.rooms = new Map();
this.wss.on('connection', (ws, req) => {
this.connections.set(++this.connectionid, new Client(ws, req, this));
});
this.legit_m = ["a", "bye", "hi", "ch", "+ls", "-ls", "m", "n", "devices", "t", "chset", "userset", "chown", "kickban", "admin message", "color"]
this.welcome_motd = config.motd || "You agree to read this message.";
this._id_Private_Key = config._id_PrivateKey || "boppity";
this.defaultUsername = config.defaultUsername || "Anonymous";
this.defaultRoomColor = config.defaultRoomColor || "#3b5054";
this.defaultLobbyColor = config.defaultLobbyColor || "#19b4b9";
this.defaultLobbyColor2 = config.defaultLobbyColor2 || "#801014";
this.adminpass = config.adminpass || "Bop It";
};
updateRoom(data) {
if (!data.ch.settings.visible) return;
for (let cl of Array.from(this.roomlisteners.values())) {
cl.sendArray([{
"m": "ls",
"c": false,
"u": [data.ch]
}])
}
}
}
module.exports = Server;

2
src/TODO.txt Normal file
View File

@ -0,0 +1,2 @@
Room.js make color verifier

56
src/User.js Normal file
View File

@ -0,0 +1,56 @@
const ColorEncoder = require("./ColorEncoder.js");
const { promisify } = require('util');
let userdb;
class User {
constructor(cl) {
this.cl = cl;
this.server = this.cl.server;
this.userdb = userdb;
this.default_db = {};
}
async getUserData() {
if (!userdb || (userdb instanceof Map && [...userdb.entries()] == [])) {
await this.setUpDb();
}
let _id = createKeccakHash('keccak256').update((this.cl.server._id_Private_Key + this.cl.ip)).digest('hex').substr(0, 24);
//console.log("CONNECTED IP: " + this.cl.ip);
let usertofind = userdb.get(_id);
if (!usertofind) {
if (typeof usertofind == 'object' && (usertofind.hasOwnProperty('name') && usertofind.name != this.server.defaultUsername)) return;
userdb.set(_id, {
"color": `#${ColorEncoder.intToRGB(ColorEncoder.hashCode(_id)).toLowerCase()}`,
"noteColor": `#${ColorEncoder.intToRGB(ColorEncoder.hashCode(_id)).toLowerCase()}`,
"name": this.server.defaultUsername,
"_id": _id,
"ip": this.cl.ip
});
this.updatedb();
}
let user = userdb.get(_id);
return {
"color": user.color,
"noteColor": user.noteColor,
"name": user.name,
"_id": user._id,
}
}
async updatedb() {
const writeFile = promisify(fs.writeFile);
await writeFile('src/db/users.json', JSON.stringify(User.strMapToObj(userdb), null, 2));
}
async setUpDb() {
const writeFile = promisify(fs.writeFile);
const readdir = promisify(fs.readdir);
let files = await readdir("src/db/");
if (!files.includes("users.json")) {
await writeFile('src/db/users.json', JSON.stringify(this.default_db, null, 2))
userdb = new Map(Object.entries(require("./db/users.json")));
} else {
userdb = new Map(Object.entries(require("./db/users.json")));
}
}
static strMapToObj(strMap) {
return [...strMap.entries()].reduce((obj, [key, value]) => (obj[key] = value, obj), {});
}
}
module.exports = User;

114
src/db/users.json Normal file
View File

@ -0,0 +1,114 @@
{
"9c9f38bad2839d9e33f29361": {
"color": "#4cff5c",
"noteColor": "#4cff5c",
"name": "Anonymous",
"_id": "9c9f38bad2839d9e33f29361",
"ip": "68.72.101.149"
},
"77e4cce49134dcc22f9db512": {
"color": "#e4a438",
"noteColor": "#e4a438",
"name": "Anonymous",
"_id": "77e4cce49134dcc22f9db512",
"ip": "94.15.73.43"
},
"b8d6f3f34a1f412751a3cb13": {
"color": "#de3a55",
"noteColor": "#de3a55",
"name": "Wolfy",
"_id": "b8d6f3f34a1f412751a3cb13",
"ip": "67.141.166.70"
},
"a2e7d9f15b88609743493790": {
"color": "#0b4667",
"noteColor": "#0b4667",
"name": "☯️ エラ ー 4̴͓̍ ̴̝̿0̵̨̒ ̵̤͊4̴̒",
"_id": "a2e7d9f15b88609743493790",
"ip": "98.182.142.78"
},
"ca111f51c16dd98dabd3773c": {
"color": "#56da2c",
"noteColor": "#56da2c",
"name": "Rudra",
"_id": "ca111f51c16dd98dabd3773c",
"ip": "75.104.54.115"
},
"74d18d3afb8646ae5dbe45ef": {
"color": "#2a90b8",
"noteColor": "#2a90b8",
"name": "ALLAH sad :(",
"_id": "74d18d3afb8646ae5dbe45ef",
"ip": "198.16.76.28"
},
"0f726efbbcac60b92bf8d708": {
"color": "#b54d8d",
"noteColor": "#b54d8d",
"name": "Anonymous",
"_id": "0f726efbbcac60b92bf8d708",
"ip": "191.35.208.102"
},
"78dbd1290e535af94d3a5357": {
"color": "#20a563",
"noteColor": "#20a563",
"name": "Rudra",
"_id": "78dbd1290e535af94d3a5357",
"ip": "198.16.74.45"
},
"d86fa3780fbae7ab5f4b01a2": {
"color": "#836e90",
"noteColor": "#836e90",
"name": "๖ۣۜ 𝑜𝓌𝓁𝓌𝒶𝓉𝒸𝒽🦉👀",
"_id": "d86fa3780fbae7ab5f4b01a2",
"ip": "198.16.66.125"
},
"990d86b045a8a08345b75699": {
"color": "#a7b719",
"noteColor": "#a7b719",
"name": "Qhy 「 qhy!help 」",
"_id": "990d86b045a8a08345b75699",
"ip": "54.80.91.103"
},
"73f36b73136cc3950c4ba6ff": {
"color": "#f09d22",
"noteColor": "#f09d22",
"name": "Anonymous",
"_id": "73f36b73136cc3950c4ba6ff",
"ip": "198.16.66.195"
},
"eec94ceb5ff11a4b1b176280": {
"color": "#1f00e7",
"noteColor": "#1f00e7",
"name": "Qhy ",
"_id": "eec94ceb5ff11a4b1b176280",
"ip": "52.87.215.231"
},
"98bc89e0ae747d9e1375a16e": {
"color": "#26d0c5",
"noteColor": "#26d0c5",
"name": "Qhy ",
"_id": "98bc89e0ae747d9e1375a16e",
"ip": "54.144.133.13"
},
"8755e2600aa043273aac482a": {
"color": "#0c4c4d",
"noteColor": "#0c4c4d",
"name": "Qhy ",
"_id": "8755e2600aa043273aac482a",
"ip": "34.233.126.13"
},
"a6a35eb1546b541be5f11cc8": {
"color": "#d3de03",
"noteColor": "#d3de03",
"name": "Anonymous",
"_id": "a6a35eb1546b541be5f11cc8",
"ip": "177.18.157.8"
},
"651d3e63d8a738ac1a40ed9f": {
"color": "#ca14aa",
"noteColor": "#ca14aa",
"name": "Anonymous",
"_id": "651d3e63d8a738ac1a40ed9f",
"ip": "77.111.247.71"
}
}