basic channels, users. Clients stack

This commit is contained in:
BopItFreak 2019-06-06 13:22:43 -04:00
parent c7f398788e
commit 3d4c8430cf
12 changed files with 344 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
*.json

View File

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

18
index.js Normal file
View File

@ -0,0 +1,18 @@
//call new Server
global.WebSocket = require('ws');
global.EventEmitter = require('events').EventEmitter;
global.fs = require('fs');
global.createKeccakHash = require('keccak');
const AsyncConsole = require('asyncconsole')
let Server = require("./src/Server.js");
global.SERVER = new Server({
port: 8080
});
let console = new AsyncConsole("", input => {
try {
console.log(JSON.stringify(eval(input)));
} catch(e) {
console.log(e.toString());
}
})

81
src/Client.js Normal file
View File

@ -0,0 +1,81 @@
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.bindEventListeners();
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.server.rooms.get(_id)) {
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();
this.user;
this.participantId;
this.channel;
this.connectionid;
this.server.connections.delete(this.connectionid);
if (this.channel) {
this.channel.emit("bye", this)
}
console.log(`Removed Connection ${this.connectionid}.`);
}
bindEventListeners() {
this.ws.on("message", (evt) => {
try {
var transmission = JSON.parse(evt);
} catch (e) {
this.destroy();
} finally {
for (let msg of transmission) {
if (!msg.hasOwnProperty("m")) return;
if (!this.server.legit_m.includes(msg.m)) return;
this.emit(msg.m, msg);
console.log(`RECIEVE: `, JSON.colorStringify(msg));
}
}
});
this.ws.on("close", () => {
this.destroy();
});
this.ws.addEventListener("error", (err) => {
console.error(err);
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};

32
src/Message.js Normal file
View File

@ -0,0 +1,32 @@
const User = require("./User.js");
const Room = require("./Room.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 = "1.0 Alpha";
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 (!msg.hasOwnProperty("set")) msg.set = {};
if (msg.hasOwnProperty("_id") && typeof msg._id == "string") {
if (msg._id.length > 512) return;
cl.setChannel(msg._id, msg.set);
}
})
}

1
src/Quota.js Normal file
View File

@ -0,0 +1 @@
//noteQuota.js

105
src/Room.js Normal file
View File

@ -0,0 +1,105 @@
//array of rooms
//room class
//room deleter
//databases in Map
class Room extends EventEmitter { //clean?
constructor(server, _id, settings) {
super();
EventEmitter.call(this);
this._id = _id;
this.server = server;
this.settings = {
lobby: true,
visible: settings.visible || true,
crownsolo: settings.crownsolo || false,
chat: settings.chat || true,
color: this.verifyColor(settings.color) || this.server.defaultRoomColor,
color2: this.verifyColor(settings.color) || this.defaultLobbyColor2
}
this.ppl = new Map();
this.bindEventListeners();
this.server.rooms.set(_id, this);
}
join(cl) {
let participantId = createKeccakHash('keccak256').update((Math.random().toString() + cl.ip)).digest('hex').substr(0, 24);
cl.user.id = participantId;
cl.participantId = participantId;
this.ppl.set(participantId, 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)
this.updateCh(cl);
}
remove(p) {
//this.participants.splice(this.participants.findIndex((cl) => cl.participantId == p.participantId), 1);
this.ppl.delete(p.participantId);
this.sendArray([{
m: "bye",
p: p.participantId
}]);
this.updateCh();
}
updateCh(cl) {
if (this.ppl.keys().next().value.length <= 0) this.destroy();
this.ppl.forEach((usr) => {
this.server.connections.get(usr.connectionid).sendArray([this.fetchData(usr, cl)])
})
}
destroy() {
this._id;
console.log(`Deleted room ${this._id}`);
this.settings = {};
this.ppl;
this.server.rooms.delete(_id);
}
sendArray(arr, not) {
this.ppl.forEach((usr) => {
if (!not || usr.participantId != not.participantId) {
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",
ch: {
count: chppl.length,
settings: this.settings,
_id: this._id
},
ppl: chppl
}
if (cl) {
if (usr.user.id == cl.user.id) {
data.p = cl.participantId;
}
}
return data;
}
verifyColor(color) {
return color; //TODO make this
}
bindEventListeners() {
this.on("bye", participant => {
this.remove(participant);
})
}
}
module.exports = Room;

29
src/Server.js Normal file
View File

@ -0,0 +1,29 @@
const Client = require("./Client.js")
class Server extends EventEmitter {
constructor(config) {
super();
EventEmitter.call(this);
this.wss = new WebSocket.Server({
port: config.port,
backlog: 100,
verifyClient: function (info, done) {
done(true)
}
});
this.connectionid = 0;
this.connections = 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"]
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";
};
}
module.exports = Server;

2
src/TODO.txt Normal file
View File

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

58
src/User.js Normal file
View File

@ -0,0 +1,58 @@
const ColorEncoder = require("./ColorEncoder.js");
class User {
constructor(cl) {
this.cl = cl;
this.userdb;
this.server = this.cl.server;
this.default_db = {}
}
async getUserData() {
if (!this.userdb) {
this.setUpDb();
}
let _id = createKeccakHash('keccak256').update((this.cl.server._id_Private_Key + this.cl.ip)).digest('hex').substr(0, 24);
if (!this.userdb.get(_id)) {
this.userdb.set(_id, {
"color": `#${ColorEncoder.intToRGB(ColorEncoder.hashCode(_id)).toLowerCase()}`,
"name": this.server.defaultUsername,
"_id": _id,
"ip": this.cl.ip
});
this.updatedb();
}
let user = this.userdb.get(_id);
return {
"color": user.color,
"name": user.name,
"_id": user._id,
}
}
updatedb() {
fs.writeFileSync('src/db/users.json', JSON.stringify(this.strMapToObj(this.userdb), null, 2), (err) => {
if (err) {
throw err;
}
});
}
strMapToObj(strMap) {
let obj = Object.create(null);
for (let [k, v] of strMap) {
obj[k] = v;
}
return obj;
}
setUpDb() {
let files = fs.readdirSync("src/db/");
if (!files.includes("users.json")) {
fs.writeFileSync('src/db/users.json', JSON.stringify(this.default_db, null, 2), (err) => {
if (err) {
throw err;
}
});
this.userdb = new Map(Object.entries(require("./db/users.json")));
} else {
this.userdb = new Map(Object.entries(require("./db/users.json")));
}
}
}
module.exports = User;

0
src/db/textfile.txt Normal file
View File